Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions docs-src/src/components/trigger-event.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,101 @@ export type RedditEventType =
| 'Lead'
| 'Purchase';

const CONVERSION_WORKER_URL = 'https://rxdb-events.daniel-meyer-e90.workers.dev';
/**
* Written by storeAdClickId() in Root.tsx when the user lands with a
* gclid/gbraid/wbraid URL param. Shape: { k, v, t }.
*/
export const AD_CLICK_STORAGE_ID = 'click_id';

function getStoredAdClickId(): { k: string; v: string; t: number; } | null {
try {
const raw = localStorage.getItem(AD_CLICK_STORAGE_ID);
if (!raw) {
return null;
}
const parsed = JSON.parse(raw);
if (!parsed || !parsed.v) {
return null;
}
return parsed;
} catch (err) {
return null;
}
}

/**
* Self-minted stable GA4-style client id. Only used when no _ga cookie
* exists, so the worker can forward events to the GA4 Measurement Protocol.
*/
function getOrMintClientId(): string {
let cid = localStorage.getItem('worker_cid');
if (!cid) {
cid = Math.floor(Math.random() * 1e10) + '.' + Math.floor(Date.now() / 1000);
localStorage.setItem('worker_cid', cid);
}
return cid;
}

function getSessionId(): string {
try {
let sid = sessionStorage.getItem('worker_sid');
if (!sid) {
sid = Math.floor(Date.now() / 1000) + '';
sessionStorage.setItem('worker_sid', sid);
}
return sid;
} catch (err) {
return Math.floor(Date.now() / 1000) + '';
}
}

function postToWorker(path: string, payload: any) {
const body = JSON.stringify(payload);
const url = CONVERSION_WORKER_URL + path;
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body);
} else {
fetch(url, { method: 'POST', body, keepalive: true }).catch(() => { });
}
}

/**
* Sends every tracking event to the conversion worker:
* - to /api/google-ads when an ad click id is stored, so Google Ads can
* import the event as an offline conversion (which event names count, and
* whether they are primary or secondary, is decided in Google Ads by which
* conversion actions exist),
* - to /api/google-analytics when no google-analytics cookie exists (gtag
* blocked or never loaded), so the worker forwards the event to the GA4
* Measurement Protocol. Users with a _ga cookie already report via gtag;
* skipping them here prevents double counting.
*/
function sendToConversionWorker(type: string, value: number) {
try {
const adClick = getStoredAdClickId();
if (adClick) {
postToWorker('/api/google-ads', {
type,
value,
clid: adClick.v,
clidKind: adClick.k
});
}
if (!document.cookie.includes('_ga=')) {
postToWorker('/api/google-analytics', {
type,
value,
cid: getOrMintClientId(),
sid: getSessionId()
});
}
} catch (err) {
console.log('# Error on conversion-worker trigger:');
console.dir(err);
}
}

export function triggerTrackingEvent(
type: string,
value: number,
Expand Down Expand Up @@ -40,6 +135,12 @@ export function triggerTrackingEvent(

console.log('triggerTrackingEvent(' + type + ', ' + value + ', redditEventType=' + redditEventType + ' ' + triggeredBefore + '/' + maxPerUser + ')');

/**
* Google Ads conversion worker (runs after the same frequency capping
* as the other trackers).
*/
sendToConversionWorker(type, value);

/**
* Reddit does not have a concept of conversion-value
* so we only track primary events because otherwise everything would
Expand Down
4 changes: 2 additions & 2 deletions docs-src/src/theme/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { onCopy, triggerTrackingEvent } from '../components/trigger-event';
import { AD_CLICK_STORAGE_ID, onCopy, triggerTrackingEvent } from '../components/trigger-event';
import { randomNumber } from '../../../plugins/utils';
import { IconClose } from '../components/icons/close';
import { Button } from '../components/button';
Expand Down Expand Up @@ -317,7 +317,7 @@ function storeAdClickId() {
for (const k of ['gclid', 'gbraid', 'wbraid']) {
const v = p.get(k);
if (v) {
localStorage.setItem('click_id', JSON.stringify({ k, v, t: Date.now() }));
localStorage.setItem(AD_CLICK_STORAGE_ID, JSON.stringify({ k, v, t: Date.now() }));
triggerTrackingEvent('click_id_' + k, 0.01, 1);
}
}
Expand Down
Loading