From 778e1a02f5c7b5fc8193cc7fc562b3a406196214 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:23:20 +0000 Subject: [PATCH 1/3] fix(replay): give the recording placeholder buttons visible feedback Remove Reload from the terminal no-full-snapshot screen, because a reload cannot bring that data back. On the still-ingesting screen, the button is now "Check for new data". It shows a loading state, starts a source listing even while a background poll is in flight, and then tells the viewer that the recording is not ready yet. Generated-By: PostHog Desktop Task-Id: a7a258da-cc35-45c3-be8b-8e3070fed5a8 --- .../session-recordings/player/PurePlayer.tsx | 22 ++++----- ...ssionRecordingDataCoordinatorLogic.test.ts | 17 +++++++ .../sessionRecordingDataCoordinatorLogic.ts | 48 ++++++++++++++++++- 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/frontend/src/scenes/session-recordings/player/PurePlayer.tsx b/frontend/src/scenes/session-recordings/player/PurePlayer.tsx index a8663d43e20e..f4b0e7d8d9ec 100644 --- a/frontend/src/scenes/session-recordings/player/PurePlayer.tsx +++ b/frontend/src/scenes/session-recordings/player/PurePlayer.tsx @@ -109,8 +109,9 @@ export function PurePlayer({ noMeta = false, noBorder = false }: PurePlayerProps isRecordingDeleted, recordingDeletedAt, recordingDeletedBy, + snapshotCheckState, } = useValues(sessionRecordingDataCoordinatorLogic(logicProps)) - const { loadSnapshots } = useActions(sessionRecordingDataCoordinatorLogic(logicProps)) + const { checkForNewSnapshots } = useActions(sessionRecordingDataCoordinatorLogic(logicProps)) const { isPlaylistCollapsed, showMetadataFooter } = useValues(playerSettingsLogic) const { setPlaylistCollapsed } = useActions(playerSettingsLogic) @@ -368,18 +369,11 @@ export function PurePlayer({ noMeta = false, noBorder = false }: PurePlayerProps The snapshot of the screen taken when this recording started never reached PostHog, so there is nothing to play back. This usually happens when the browser is closed or goes offline before the recording finishes - uploading.{' '} + uploading. Reloading won't bring this data back.{' '} Learn more
-+ It's not ready to play yet. Try again in a minute. +
+ ) : null} > )} diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.test.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.test.ts index aad51e2b3d55..82daac8e2c67 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.test.ts @@ -422,6 +422,23 @@ describe('sessionRecordingDataCoordinatorLogic', () => { isOldAndInvalid: logic.values.isOldAndInvalid, }).toEqual(expected) }) + it('settles a viewer-requested check for new data while a background poll is in flight', async () => { + mountWithSnapshots(incrementalOnlySnapshotsAsJSONLines(dayjs().subtract(1, 'minute').valueOf())) + await loadFully() + expect(logic.values.snapshotCheckState).toBe('idle') + + jest.useFakeTimers() + try { + logic.actions.loadSnapshotSources(60000) + logic.actions.checkForNewSnapshots() + expect(logic.values.snapshotCheckState).toBe('checking') + + await jest.advanceTimersByTimeAsync(1000) + expect(logic.values.snapshotCheckState).toBe('checked') + } finally { + jest.useRealTimers() + } + }) }) // TODO need deduplication tests for blob_v2 sources before we deprecate blob_v1 diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.ts index b7f05288e87d..4c08e1bb316d 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataCoordinatorLogic.ts @@ -238,6 +238,11 @@ export function isWithinIngestionGracePeriod(start: Dayjs | null): boolean { return start != null && now().diff(start, 'minute') <= INGESTION_GRACE_PERIOD_MINUTES } +export type SnapshotCheckState = 'idle' | 'checking' | 'checked' + +const settleSnapshotCheck = (state: SnapshotCheckState): SnapshotCheckState => + state === 'checking' ? 'checked' : state + // Generated by kea-typegen. Update if you're an agent, ignore if you're human. export interface sessionRecordingDataCoordinatorLogicValues { sessionComments: CommentType[] // commentsLogic @@ -283,6 +288,7 @@ export interface sessionRecordingDataCoordinatorLogicValues { reportedLoaded: boolean segments: RecordingSegment[] sessionPlayerData: SessionPlayerData + snapshotCheckState: SnapshotCheckState snapshots: RecordingSnapshot[] snapshotsByWindowId: Record