fix(analytics): resolve sessionId before assigning trackScreen state (v1.12.1+17)
The sessionId getter mints a fresh session on cold start or after a 30-min lapse, and as a side effect nulls _currentScreenName/_currentStepUid. trackScreen() set those fields before calling the sessionId getter, so the mint-on-cold-start side effect could immediately null the fields it had just set, breaking the same-screen idempotency check and firing a duplicate session_enter right after cold start. Fix: resolve visitorId and sessionId into local variables before assigning _currentScreenName/_currentStepUid, and use the locals in the _enqueue() payload. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,13 @@
|
|||||||
/// `flutter_screen_views` row per screen; this is a Phase-1 backend gap,
|
/// `flutter_screen_views` row per screen; this is a Phase-1 backend gap,
|
||||||
/// not a client bug, and applies identically here).
|
/// not a client bug, and applies identically here).
|
||||||
///
|
///
|
||||||
/// Version: 1.0.0 | Created: 2026-09-25
|
/// Version: 1.0.1 | Created: 2026-09-25 | Updated: 2026-09-25
|
||||||
|
/// v1.0.1: trackScreen() resolved visitorId/sessionId into locals
|
||||||
|
/// (resolvedVisitorId/resolvedSessionId) BEFORE assigning
|
||||||
|
/// _currentScreenName/_currentStepUid, so the sessionId getter's
|
||||||
|
/// mint-on-cold-start side effect (which nulls those fields) can no
|
||||||
|
/// longer race the same-screen idempotency check and cause a duplicate
|
||||||
|
/// session_enter right after cold start.
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@@ -365,6 +371,17 @@ class SessionJourney extends WidgetsBindingObserver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_leaveCurrentScreen();
|
_leaveCurrentScreen();
|
||||||
|
// Resolve visitorId/sessionId BEFORE assigning _currentScreenName:
|
||||||
|
// sessionId's getter mints (and persists) a fresh session on a lapsed
|
||||||
|
// or cold window, and as a documented side effect of that mint it
|
||||||
|
// nulls _currentScreenName/_currentStepUid so the next trackScreen()
|
||||||
|
// re-enters cleanly under the new session. Reading it after already
|
||||||
|
// setting _currentScreenName below would immediately undo that
|
||||||
|
// assignment on every cold start, breaking this method's own
|
||||||
|
// same-screen idempotency and causing a duplicate session_enter on
|
||||||
|
// the very next call for the same screen. Found and fixed 2026-09-25.
|
||||||
|
final resolvedVisitorId = visitorId;
|
||||||
|
final resolvedSessionId = sessionId;
|
||||||
final stepUid = newUuidV4();
|
final stepUid = newUuidV4();
|
||||||
_currentScreenName = screenName;
|
_currentScreenName = screenName;
|
||||||
_currentStepUid = stepUid;
|
_currentStepUid = stepUid;
|
||||||
@@ -373,8 +390,8 @@ class SessionJourney extends WidgetsBindingObserver {
|
|||||||
_enqueue({
|
_enqueue({
|
||||||
'action': 'session_enter',
|
'action': 'session_enter',
|
||||||
'appKey': sessionJourneyAppKey,
|
'appKey': sessionJourneyAppKey,
|
||||||
'visitorId': visitorId,
|
'visitorId': resolvedVisitorId,
|
||||||
'sessionId': sessionId,
|
'sessionId': resolvedSessionId,
|
||||||
'stepUid': stepUid,
|
'stepUid': stepUid,
|
||||||
'platform': kIsWeb ? 'web' : 'unknown',
|
'platform': kIsWeb ? 'web' : 'unknown',
|
||||||
'appVersion': appVersion,
|
'appVersion': appVersion,
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
name: m8chat_app
|
name: m8chat_app
|
||||||
description: "M8Chat — Matrix chat client for Android, iOS, and Web."
|
description: "M8Chat — Matrix chat client for Android, iOS, and Web."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.12.0+16
|
version: 1.12.1+17
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.11.0 <4.0.0'
|
sdk: '>=3.11.0 <4.0.0'
|
||||||
|
|||||||
Reference in New Issue
Block a user