diff --git a/src/app/(main)/websites/[websiteId]/replays/[sessionId]/ReplayPlayer.tsx b/src/app/(main)/websites/[websiteId]/replays/[sessionId]/ReplayPlayer.tsx index ba5d66983..04bb2345a 100644 --- a/src/app/(main)/websites/[websiteId]/replays/[sessionId]/ReplayPlayer.tsx +++ b/src/app/(main)/websites/[websiteId]/replays/[sessionId]/ReplayPlayer.tsx @@ -4,24 +4,6 @@ import { useEffect, useRef, useState } from 'react'; import { useMobile } from '@/components/hooks'; import 'rrweb-player/dist/style.css'; -function normalizeEvents(events: any[], maxGapMs = 60_000): any[] { - if (events.length < 2) return events; - - const sorted = [...events].sort((a, b) => a.timestamp - b.timestamp); - let shift = 0; - const result: any[] = [sorted[0]]; - - for (let i = 1; i < sorted.length; i++) { - const gap = sorted[i].timestamp - sorted[i - 1].timestamp; - if (gap > maxGapMs) { - shift += gap - 1000; - } - result.push(shift > 0 ? { ...sorted[i], timestamp: sorted[i].timestamp - shift } : sorted[i]); - } - - return result; -} - export function ReplayPlayer({ events }: { events: any[] }) { const containerRef = useRef(null); const playerRef = useRef(null); @@ -44,7 +26,7 @@ export function ReplayPlayer({ events }: { events: any[] }) { playerRef.current = new RRWebPlayer({ target: containerRef.current, props: { - events: normalizeEvents(events), + events: events, width: playerWidth, height: playerHeight, autoPlay: false, diff --git a/src/app/api/websites/[websiteId]/replays/[sessionId]/route.ts b/src/app/api/websites/[websiteId]/replays/[sessionId]/route.ts index 01e3a91aa..1cca77510 100644 --- a/src/app/api/websites/[websiteId]/replays/[sessionId]/route.ts +++ b/src/app/api/websites/[websiteId]/replays/[sessionId]/route.ts @@ -1,3 +1,4 @@ +import { stitchChunkEvents } from '@/lib/replay'; import { parseRequest } from '@/lib/request'; import { json, unauthorized } from '@/lib/response'; import { canViewWebsite } from '@/permissions'; @@ -21,7 +22,7 @@ export async function GET( const chunks = await getReplayChunks(websiteId, sessionId); - const allEvents = chunks.flatMap(chunk => chunk.events); + const allEvents = stitchChunkEvents(chunks); const startedAt = chunks.length > 0 ? chunks[0].startedAt : null; const endedAt = chunks.length > 0 ? chunks[chunks.length - 1].endedAt : null; diff --git a/src/lib/replay.ts b/src/lib/replay.ts new file mode 100644 index 000000000..8a3c7574c --- /dev/null +++ b/src/lib/replay.ts @@ -0,0 +1,17 @@ +import type { ReplayChunk } from '@/queries/sql'; + +export function stitchChunkEvents(chunks: ReplayChunk[]): any[] { + if (!chunks.length) return []; + const sorted = [...chunks].sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime()); + const result: any[] = []; + let timeOffset = 0; + for (const chunk of sorted) { + const chunkStartMs = chunk.startedAt.getTime(); + const events = [...chunk.events].sort((a, b) => a.timestamp - b.timestamp); + for (const event of events) { + result.push({ ...event, timestamp: timeOffset + (event.timestamp - chunkStartMs) }); + } + timeOffset += chunk.endedAt.getTime() - chunkStartMs + 1000; + } + return result; +} diff --git a/src/queries/sql/replays/getSessionReplays.ts b/src/queries/sql/replays/getSessionReplays.ts index 7f650acff..ce4c49e4c 100644 --- a/src/queries/sql/replays/getSessionReplays.ts +++ b/src/queries/sql/replays/getSessionReplays.ts @@ -53,7 +53,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { count(sr.replay_id) as "chunkCount", min(sr.started_at) as "startedAt", max(sr.ended_at) as "endedAt", - (extract(epoch from max(sr.ended_at) - min(sr.started_at)) * 1000)::bigint as "duration", + sum(extract(epoch from sr.ended_at - sr.started_at) * 1000)::bigint as "duration", max(sr.created_at) as "createdAt" from session_replay sr join session on session.session_id = sr.session_id @@ -109,7 +109,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) { count(session_replay.replay_id) as chunkCount, min(session_replay.started_at) as startedAt, max(session_replay.ended_at) as endedAt, - toInt64(dateDiff('millisecond', min(session_replay.started_at), max(session_replay.ended_at))) as duration, + toInt64(sum(dateDiff('millisecond', session_replay.started_at, session_replay.ended_at))) as duration, max(session_replay.created_at) as createdAt from session_replay join (