@@ -54,6 +54,10 @@ import { sampleOnProperty } from '../sampling'
54
54
const LOGGER_PREFIX = '[SessionRecording]'
55
55
const logger = createLogger ( LOGGER_PREFIX )
56
56
57
+ function getRRWebRecord ( ) : rrwebRecord | undefined {
58
+ return assignableWindow ?. __PosthogExtensions__ ?. rrweb ?. record
59
+ }
60
+
57
61
type SessionStartReason =
58
62
| 'sampling_overridden'
59
63
| 'recording_initialized'
@@ -256,7 +260,7 @@ export class SessionRecording {
256
260
private _captureStarted : boolean
257
261
private stopRrweb : listenerHandler | undefined
258
262
private receivedDecide : boolean
259
- private isIdle = false
263
+ private isIdle : boolean | 'unknown' = 'unknown'
260
264
261
265
private _linkedFlagSeen : boolean = false
262
266
private _lastActivityTimestamp : number = Date . now ( )
@@ -290,10 +294,6 @@ export class SessionRecording {
290
294
return this . instance . config . session_recording . session_idle_threshold_ms || RECORDING_IDLE_THRESHOLD_MS
291
295
}
292
296
293
- private get rrwebRecord ( ) : rrwebRecord | undefined {
294
- return assignableWindow ?. __PosthogExtensions__ ?. rrweb ?. record
295
- }
296
-
297
297
public get started ( ) : boolean {
298
298
// TODO could we use status instead of _captureStarted?
299
299
return this . _captureStarted
@@ -795,7 +795,7 @@ export class SessionRecording {
795
795
796
796
// If recorder.js is already loaded (if array.full.js snippet is used or posthog-js/dist/recorder is
797
797
// imported), don't load script. Otherwise, remotely import recorder.js from cdn since it hasn't been loaded.
798
- if ( ! this . rrwebRecord ) {
798
+ if ( ! getRRWebRecord ( ) ) {
799
799
assignableWindow . __PosthogExtensions__ ?. loadExternalDependency ?.( this . instance , this . scriptName , ( err ) => {
800
800
if ( err ) {
801
801
return logger . error ( 'could not load recorder' , err )
@@ -863,13 +863,18 @@ export class SessionRecording {
863
863
if ( isUserInteraction ) {
864
864
this . _lastActivityTimestamp = event . timestamp
865
865
if ( this . isIdle ) {
866
+ const idleWasUnknown = this . isIdle === 'unknown'
866
867
// Remove the idle state
867
868
this . isIdle = false
868
- this . _tryAddCustomEvent ( 'sessionNoLongerIdle' , {
869
- reason : 'user activity' ,
870
- type : event . type ,
871
- } )
872
- returningFromIdle = true
869
+ // if the idle state was unknown, we don't want to add an event, since we're just in bootup
870
+ // whereas if it was true, we know we've been idle for a while, and we can mark ourselves as returning from idle
871
+ if ( ! idleWasUnknown ) {
872
+ this . _tryAddCustomEvent ( 'sessionNoLongerIdle' , {
873
+ reason : 'user activity' ,
874
+ type : event . type ,
875
+ } )
876
+ returningFromIdle = true
877
+ }
873
878
}
874
879
}
875
880
@@ -918,11 +923,11 @@ export class SessionRecording {
918
923
}
919
924
920
925
private _tryAddCustomEvent ( tag : string , payload : any ) : boolean {
921
- return this . _tryRRWebMethod ( newQueuedEvent ( ( ) => this . rrwebRecord ! . addCustomEvent ( tag , payload ) ) )
926
+ return this . _tryRRWebMethod ( newQueuedEvent ( ( ) => getRRWebRecord ( ) ! . addCustomEvent ( tag , payload ) ) )
922
927
}
923
928
924
929
private _tryTakeFullSnapshot ( ) : boolean {
925
- return this . _tryRRWebMethod ( newQueuedEvent ( ( ) => this . rrwebRecord ! . takeFullSnapshot ( ) ) )
930
+ return this . _tryRRWebMethod ( newQueuedEvent ( ( ) => getRRWebRecord ( ) ! . takeFullSnapshot ( ) ) )
926
931
}
927
932
928
933
private _onScriptLoaded ( ) {
@@ -972,7 +977,8 @@ export class SessionRecording {
972
977
sessionRecordingOptions . blockSelector = this . masking . blockSelector ?? undefined
973
978
}
974
979
975
- if ( ! this . rrwebRecord ) {
980
+ const rrwebRecord = getRRWebRecord ( )
981
+ if ( ! rrwebRecord ) {
976
982
logger . error (
977
983
'onScriptLoaded was called but rrwebRecord is not available. This indicates something has gone wrong.'
978
984
)
@@ -981,7 +987,7 @@ export class SessionRecording {
981
987
982
988
this . mutationRateLimiter =
983
989
this . mutationRateLimiter ??
984
- new MutationRateLimiter ( this . rrwebRecord , {
990
+ new MutationRateLimiter ( rrwebRecord , {
985
991
refillRate : this . instance . config . session_recording . __mutationRateLimiterRefillRate ,
986
992
bucketSize : this . instance . config . session_recording . __mutationRateLimiterBucketSize ,
987
993
onBlockedNode : ( id , node ) => {
@@ -995,7 +1001,7 @@ export class SessionRecording {
995
1001
} )
996
1002
997
1003
const activePlugins = this . _gatherRRWebPlugins ( )
998
- this . stopRrweb = this . rrwebRecord ( {
1004
+ this . stopRrweb = rrwebRecord ( {
999
1005
emit : ( event ) => {
1000
1006
this . onRRwebEmit ( event )
1001
1007
} ,
@@ -1005,7 +1011,8 @@ export class SessionRecording {
1005
1011
1006
1012
// We reset the last activity timestamp, resetting the idle timer
1007
1013
this . _lastActivityTimestamp = Date . now ( )
1008
- this . isIdle = false
1014
+ // stay unknown if we're not sure if we're idle or not
1015
+ this . isIdle = isBoolean ( this . isIdle ) ? this . isIdle : 'unknown'
1009
1016
1010
1017
this . _tryAddCustomEvent ( '$session_options' , {
1011
1018
sessionRecordingOptions,
@@ -1022,7 +1029,7 @@ export class SessionRecording {
1022
1029
clearInterval ( this . _fullSnapshotTimer )
1023
1030
}
1024
1031
// we don't schedule snapshots while idle
1025
- if ( this . isIdle ) {
1032
+ if ( this . isIdle === true ) {
1026
1033
return
1027
1034
}
1028
1035
@@ -1109,7 +1116,8 @@ export class SessionRecording {
1109
1116
this . _updateWindowAndSessionIds ( event )
1110
1117
1111
1118
// When in an idle state we keep recording, but don't capture the events,
1112
- if ( this . isIdle && ! isSessionIdleEvent ( event ) ) {
1119
+ // we don't want to return early if idle is 'unknown'
1120
+ if ( this . isIdle === true && ! isSessionIdleEvent ( event ) ) {
1113
1121
return
1114
1122
}
1115
1123
0 commit comments