feat(analytics): dual GA4 + OpenPanel, disable Sentry profiling#2750
Conversation
react-ga4 may not properly inject the gtag.js script tag in CEF's webview, causing GA to silently no-op in production builds. Switch to the raw Google gtag.js snippet: inject the script tag, set up window.dataLayer/window.gtag, and call the gtag API directly. Also adds VITE_GA_FORCE_DEV flag so GA can be tested in dev builds.
Switch from Google Analytics (react-ga4 / raw gtag.js) to OpenPanel for usage tracking. Uses raw script injection of op1.js with the inline queue stub so calls before the script loads are buffered. OpenPanel client ID: e9c996d5-497f-4eec-9bde-630019ad525b API URL: https://panel.tinyhumans.ai/api No dev-mode gate — analytics initializes in all environments. Consent gating and event allowlist are preserved.
Both Google Analytics (raw gtag.js) and OpenPanel fire on every trackPageView/trackEvent call. Each provider initializes independently — GA requires VITE_GA_MEASUREMENT_ID, OpenPanel is always-on with the hardcoded client ID. Consent gating and event allowlist apply to both providers equally.
Set tracesSampleRate to 0 (was a dynamic sampler at 10% when consent was on). Sentry now only captures unhandled exceptions and explicit captureException calls — no performance traces or replays.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughMigrates analytics from ReactGA to raw gtag.js + OpenPanel with consent/allowlist enforcement, adds GA_FORCE_DEV config, changes Sentry sampling to tracesSampleRate: 0, updates tests for the new flow, and includes two small build/release fixes. ChangesAnalytics Provider Migration to Dual GA/OpenPanel Model
Build and Release Infrastructure Updates
Sequence DiagramsequenceDiagram
participant InitGA as initGA()
participant Document as document
participant GA as gtag.js
participant OpenPanel as op1.js
participant Window as window
participant Sentry as Sentry
InitGA->>Document: createElement('script') src=gtag.js
InitGA->>Document: createElement('script') src=op1.js
Document->>GA: load gtag.js
Document->>OpenPanel: load op1.js
GA->>Window: define window.gtag
OpenPanel->>Window: define window.op
InitGA->>Sentry: sync consent (analyticsEnabled)
Window->>Sentry: (on opt-out) flush client
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/src/services/__tests__/analytics.test.ts (1)
424-430: ⚡ Quick winAdd GA-side assertions for dual-provider contract.
These tests currently prove OpenPanel emission, but not
window.gtagemission. A smallgtagspy assertion in page-view and event tests would catch regressions where only one provider fires.Also applies to: 458-464
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/services/__tests__/analytics.test.ts` around lines 424 - 430, The test only asserts OpenPanel emission; add a spy for window.gtag and assert it was called with the matching GA arguments to enforce the dual-provider contract: in the 'sends a screen_view event when consent is on' test (where initGA() and trackPageView('/home') are used) create a vi.spyOn(window, 'gtag') and expect it to have been called with the corresponding 'event' or 'screen_view' payload for '/home'; do the same in the event test around trackEvent (the other test at 458-464) so both window.op and window.gtag emissions are verified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/services/analytics.ts`:
- Around line 312-318: The init call in app/src/services/analytics.ts currently
enables OpenPanel auto-tracking
(trackScreenViews/trackOutgoingLinks/trackAttributes: true) which causes
duplicate or un-gated events; change the window.op('init', ...) options to
disable OpenPanel auto-tracking (set those three flags to false) and rely on the
existing consent-gated manual tracking flow
(syncAnalyticsConsent/analyticsEnabled and calls to window.op('track', ...) such
as from trackPageView in App.tsx); ensure manual calls to window.op('track')
remain guarded by analyticsEnabled so enabling/disabling consent controls all
tracking.
---
Nitpick comments:
In `@app/src/services/__tests__/analytics.test.ts`:
- Around line 424-430: The test only asserts OpenPanel emission; add a spy for
window.gtag and assert it was called with the matching GA arguments to enforce
the dual-provider contract: in the 'sends a screen_view event when consent is
on' test (where initGA() and trackPageView('/home') are used) create a
vi.spyOn(window, 'gtag') and expect it to have been called with the
corresponding 'event' or 'screen_view' payload for '/home'; do the same in the
event test around trackEvent (the other test at 458-464) so both window.op and
window.gtag emissions are verified.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2cd983bc-87c3-45ae-9e48-d9521d299d2c
📒 Files selected for processing (5)
.github/workflows/build-desktop.ymlapp/src/services/__tests__/analytics.test.tsapp/src/services/analytics.tsapp/src/utils/config.tsscripts/release/strip-appimage-graphics-libs.sh
trackScreenViews/trackOutgoingLinks/trackAttributes were set to true
in op('init', ...) which bypasses the consent gate and double-counts
screen views alongside the manual trackPageView() calls. Set all
three to false so all events flow through the consent-gated manual
tracking path.
Review: PR #2750 — feat(analytics): dual GA4 + OpenPanel, disable Sentry profilingCode looks good to me. 👍 Summary
Quality
CI StatusOne failing check: Rust Core Tests (Windows — secrets ACL). This is unrelated to the PR (frontend-only changes, all TypeScript tests pass). Likely a pre-existing flake on the Windows runner. Once the Windows test passes, I'll come back and approve. Let me know if you need any help debugging it. |
graycyrus
left a comment
There was a problem hiding this comment.
Looks good, nice work!
Summary
react-ga4with raw gtag.js script injection for GA4 — more reliable in CEF webviewstracesSampleRate: 0) — errors onlyVITE_GA_FORCE_DEVconfig for local GA testingProblem
react-ga4may not properly inject the gtag.js<script>tag in Tauri's CEF webview, causing GA to silently no-op in production buildsIS_DEVcheck) prevented testing analytics locallySolution
trackPageViewandtrackEventcall; each initializes independentlyVITE_GA_MEASUREMENT_IDto be set; OpenPanel is always-on with hardcoded client IDtracesSampleRateset to0(was a dynamic sampler); replays already at 0Submission Checklist
Impact
Related
tauri.localhostoriginAI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:checkpnpm typecheckpnpm debug unit src/services/__tests__/analytics.test.ts— 24/24 passValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
Duplicate / Superseded PR Handling
Summary by CodeRabbit