Files
supabase/apps/studio/components/ui/DataTable/LiveButton.utils.ts
T
David WhittingtonandClaude Opus 5.5 3bf710eae9 feat(studio): pause unified logs live mode when nobody is watching
Live mode polled every 10 seconds for as long as the page stayed open,
including in hidden tabs and tabs left unattended for hours. Move the
polling loop into a useLivePolling hook that:

- stops polling while the tab is hidden and polls immediately when it is
  visible again
- pauses live mode after 15 minutes of visible time with no pointer, key,
  wheel, or scroll activity, with a notice to resume it
- backs off from 10s to 20s, 40s, then 60s while polls find no new rows,
  resetting on new rows, activity, or the tab becoming visible

Track clicks on the notice's "Resume live mode" button
(unified_logs_live_mode_resume_button_clicked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:02:58 -05:00

35 lines
1.4 KiB
TypeScript

export const LIVE_POLL_BASE_INTERVAL_MS = 10_000
export const LIVE_POLL_MAX_INTERVAL_MS = 60_000
export const LIVE_MODE_IDLE_TIMEOUT_MS = 15 * 60_000
/**
* Delay before the next live-mode poll. Doubles after each poll that finds no new rows
* (10s, 20s, 40s), capped at 60s, so quiet projects are polled less often.
*/
export function getLivePollDelay(consecutiveEmptyPolls: number): number {
if (!Number.isFinite(consecutiveEmptyPolls) || consecutiveEmptyPolls <= 0) {
return LIVE_POLL_BASE_INTERVAL_MS
}
return Math.min(
LIVE_POLL_BASE_INTERVAL_MS * 2 ** Math.floor(consecutiveEmptyPolls),
LIVE_POLL_MAX_INTERVAL_MS
)
}
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value !== null
/**
* Whether a live-mode poll found no new rows. A poll is `fetchPreviousPage`, which prepends
* the new page, so its result's first page holds the rows that poll returned. Anything
* unrecognized counts as not empty, so polling never slows down on unexpected data.
*/
export function isEmptyLivePoll(result: unknown): boolean {
if (!isRecord(result) || !isRecord(result.data)) return false
const pages = result.data.pages
if (!Array.isArray(pages) || pages.length === 0) return false
const newestPage = pages[0]
if (!isRecord(newestPage) || !Array.isArray(newestPage.data)) return false
return newestPage.data.length === 0
}