From 8c74738e06fbc6498e7b88edb5244c4a495bc1f9 Mon Sep 17 00:00:00 2001 From: Fabian Iwand Date: Wed, 6 Aug 2025 10:27:00 +0200 Subject: [PATCH 1/2] Fix: change handlers not passed to new room instance (#6549) Fixes #6547 Modifies `TLSocketRoom` to hold onto `onDataChange` and `onPresenceChange`, and to pass them on whenever `TLSyncRoom` is reinstantiated internally. ### Change type - [x] `bugfix` - [ ] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Test plan - [x] Unit tests - [ ] End to end tests ### Release notes - TBD --- packages/sync-core/src/lib/TLSocketRoom.ts | 12 +++++++-- .../sync-core/src/test/TLSocketRoom.test.ts | 26 ++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/sync-core/src/lib/TLSocketRoom.ts b/packages/sync-core/src/lib/TLSocketRoom.ts index 70a3f49e727e..192031791928 100644 --- a/packages/sync-core/src/lib/TLSocketRoom.ts +++ b/packages/sync-core/src/lib/TLSocketRoom.ts @@ -24,6 +24,10 @@ export class TLSocketRoom void } >() readonly log?: TLSyncLog + private readonly syncCallbacks: { + onDataChange?(): void + onPresenceChange?(): void + } constructor( public readonly opts: { @@ -65,11 +69,14 @@ export class TLSocketRoom({ + ...this.syncCallbacks, schema: opts.schema ?? (createTLSchema() as any), snapshot: initialSnapshot, - onDataChange: opts.onDataChange, - onPresenceChange: opts.onPresenceChange, log: opts.log, }) this.room.events.on('session_removed', (args) => { @@ -314,6 +321,7 @@ export class TLSocketRoom({ + ...this.syncCallbacks, schema: oldRoom.schema, snapshot: { clock: oldRoom.clock + 1, diff --git a/packages/sync-core/src/test/TLSocketRoom.test.ts b/packages/sync-core/src/test/TLSocketRoom.test.ts index 2e142326ff26..32f0028ce552 100644 --- a/packages/sync-core/src/test/TLSocketRoom.test.ts +++ b/packages/sync-core/src/test/TLSocketRoom.test.ts @@ -1,5 +1,5 @@ import { InstancePresenceRecordType, PageRecordType } from '@tldraw/tlschema' -import { createTLSchema, createTLStore } from 'tldraw' +import { createTLSchema, createTLStore, ZERO_INDEX_KEY } from 'tldraw' import { WebSocketMinimal } from '../lib/ServerSocketAdapter' import { TLSocketRoom } from '../lib/TLSocketRoom' import { RecordOpType } from '../lib/diff' @@ -241,4 +241,28 @@ describe(TLSocketRoom, () => { ) expect(documentRecordIds).toHaveLength(0) }) + + it('passes onDataChange handler through', async () => { + const addPage = (room: TLSocketRoom) => + room.updateStore((store) => { + store.put( + PageRecordType.create({ id: PageRecordType.createId(), name: '', index: ZERO_INDEX_KEY }) + ) + }) + const store = getStore() + store.ensureStoreIsUsable() + let called = 0 + + const room = new TLSocketRoom({ onDataChange: () => ++called }) + expect(called).toEqual(0) + + await addPage(room) + expect(called).toEqual(1) + + room.loadSnapshot(room.getCurrentSnapshot()) + expect(called).toEqual(1) + + await addPage(room) + expect(called).toEqual(2) + }) }) From 67f438ba104b038050ed80b5974f96928d6455f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mitja=20Bezen=C5=A1ek?= Date: Wed, 6 Aug 2025 10:41:48 +0200 Subject: [PATCH 2/2] Avoid incorrect session attribution. (#6544) My understanding is that were were setting a property called `source` when sending over the events. Since there was no other source set then GA4 took this as the traffic source attribution as well. So the problem is not actually using UTM tags for internal navigation. The problem was that were we using the same name of the property that GA4 uses for traffic attribution. ### Change type - [x] `bugfix` --- apps/dotcom/client/src/utils/analytics.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/dotcom/client/src/utils/analytics.tsx b/apps/dotcom/client/src/utils/analytics.tsx index 1de658e297f2..3dc261eb9b5e 100644 --- a/apps/dotcom/client/src/utils/analytics.tsx +++ b/apps/dotcom/client/src/utils/analytics.tsx @@ -178,6 +178,12 @@ function getGA4() { export function trackEvent(name: string, data?: { [key: string]: any }) { getPosthog()?.capture(name, data) + + // For GA4, rename 'source' to 'event_source' to avoid session attribution + if (data) { + const { source, ...rest } = data + data = source !== undefined ? { ...rest, event_source: source } : rest + } getGA4()?.event(name, data) }