Merge ticket branch t092
This commit is contained in:
+17
-97
@@ -8,7 +8,7 @@ import {
|
|||||||
saveLogSortSettings,
|
saveLogSortSettings,
|
||||||
type LogSortSettings,
|
type LogSortSettings,
|
||||||
} from './components/LogTimeline'
|
} from './components/LogTimeline'
|
||||||
import { TIME_RANGE_PRESETS, computePresetRange, type TimeRange, type TimeRangePresetId } from './lib/timeRange'
|
import { TIME_RANGE_PRESETS, type TimeRange, type TimeRangePresetId } from './lib/timeRange'
|
||||||
import { LogPreview } from './components/LogPreview'
|
import { LogPreview } from './components/LogPreview'
|
||||||
import { Params } from './components/Params'
|
import { Params } from './components/Params'
|
||||||
import { Metrics } from './components/Metrics'
|
import { Metrics } from './components/Metrics'
|
||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
type TimelineBucket,
|
type TimelineBucket,
|
||||||
} from './lib/api'
|
} from './lib/api'
|
||||||
import { DocsNavProvider } from './lib/docsNav'
|
import { DocsNavProvider } from './lib/docsNav'
|
||||||
|
import { useRangeSelection } from './lib/useRangeSelection'
|
||||||
import { useSettingsPanel } from './lib/useSettingsPanel'
|
import { useSettingsPanel } from './lib/useSettingsPanel'
|
||||||
import { POLL_MS } from './lib/polling'
|
import { POLL_MS } from './lib/polling'
|
||||||
|
|
||||||
@@ -186,47 +187,18 @@ function StandaloneRanged({
|
|||||||
initialFixedRange: TimeRange | null
|
initialFixedRange: TimeRange | null
|
||||||
}) {
|
}) {
|
||||||
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
|
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
|
||||||
const [range, setRange] = useState<TimeRange | null>(
|
|
||||||
initialPresetId ? computePresetRange(initialPresetId) : initialFixedRange,
|
|
||||||
)
|
|
||||||
// No preset and no fixed range means the popout was opened with nothing carried over (e.g. a bare URL) — default to 'all', matching FullApp's own default.
|
// No preset and no fixed range means the popout was opened with nothing carried over (e.g. a bare URL) — default to 'all', matching FullApp's own default.
|
||||||
const [activePreset, setActivePreset] = useState<TimeRangePresetId | null>(
|
const { range, activePreset, selectPreset, handleManualRangeSelect, resolveEffectiveRange } = useRangeSelection({
|
||||||
initialPresetId ?? (initialFixedRange ? null : 'all'),
|
preset: initialPresetId ?? (initialFixedRange ? null : 'all'),
|
||||||
)
|
range: initialFixedRange,
|
||||||
|
})
|
||||||
const rangeRef = useRef(range)
|
|
||||||
useEffect(() => {
|
|
||||||
rangeRef.current = range
|
|
||||||
}, [range])
|
|
||||||
|
|
||||||
function selectPreset(id: TimeRangePresetId) {
|
|
||||||
setActivePreset(id)
|
|
||||||
setRange(computePresetRange(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleManualRangeSelect(next: TimeRange | null) {
|
|
||||||
if (next === null) {
|
|
||||||
selectPreset('all')
|
|
||||||
} else {
|
|
||||||
setActivePreset(null)
|
|
||||||
setRange(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
async function poll() {
|
async function poll() {
|
||||||
// Rolling presets re-resolve on this same polling cadence instead of freezing the window at selection time — this is what makes a carried-over preset independently slide rather than track the source tab.
|
// Rolling presets re-resolve on this same polling cadence instead of freezing the window at selection time — this is what makes a carried-over preset independently slide rather than track the source tab.
|
||||||
const effectiveRange =
|
const effectiveRange = resolveEffectiveRange()
|
||||||
activePreset && activePreset !== 'all' ? computePresetRange(activePreset) : rangeRef.current
|
|
||||||
if (activePreset && activePreset !== 'all') {
|
|
||||||
setRange((current) =>
|
|
||||||
current && effectiveRange && current.startMs === effectiveRange.startMs && current.endMs === effectiveRange.endMs
|
|
||||||
? current
|
|
||||||
: effectiveRange,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const nextBuckets = await fetchTimeline({
|
const nextBuckets = await fetchTimeline({
|
||||||
startMs: effectiveRange?.startMs,
|
startMs: effectiveRange?.startMs,
|
||||||
@@ -295,8 +267,10 @@ function StandaloneLog() {
|
|||||||
const [logs, setLogs] = useState<LogEntry[]>([])
|
const [logs, setLogs] = useState<LogEntry[]>([])
|
||||||
const [params, setParams] = useState<ParamDeclaration[]>([])
|
const [params, setParams] = useState<ParamDeclaration[]>([])
|
||||||
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
|
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
|
||||||
const [range, setRange] = useState<TimeRange | null>(null)
|
const { range, activePreset, selectPreset, handleManualRangeSelect, resolveEffectiveRange } = useRangeSelection({
|
||||||
const [activePreset, setActivePreset] = useState<TimeRangePresetId | null>('all')
|
preset: 'all',
|
||||||
|
range: null,
|
||||||
|
})
|
||||||
const [filter, setFilter] = useState('')
|
const [filter, setFilter] = useState('')
|
||||||
const [filterError, setFilterError] = useState<string | null>(null)
|
const [filterError, setFilterError] = useState<string | null>(null)
|
||||||
const [sortSettings, setSortSettings] = useState<LogSortSettings>(loadLogSortSettings)
|
const [sortSettings, setSortSettings] = useState<LogSortSettings>(loadLogSortSettings)
|
||||||
@@ -307,11 +281,6 @@ function StandaloneLog() {
|
|||||||
saveLogSortSettings(next)
|
saveLogSortSettings(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
const rangeRef = useRef(range)
|
|
||||||
useEffect(() => {
|
|
||||||
rangeRef.current = range
|
|
||||||
}, [range])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchParamDeclarations()
|
fetchParamDeclarations()
|
||||||
.then(setParams)
|
.then(setParams)
|
||||||
@@ -320,33 +289,11 @@ function StandaloneLog() {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
function selectPreset(id: TimeRangePresetId) {
|
|
||||||
setActivePreset(id)
|
|
||||||
setRange(computePresetRange(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleManualRangeSelect(next: TimeRange | null) {
|
|
||||||
if (next === null) {
|
|
||||||
selectPreset('all')
|
|
||||||
} else {
|
|
||||||
setActivePreset(null)
|
|
||||||
setRange(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
async function poll() {
|
async function poll() {
|
||||||
const effectiveRange =
|
const effectiveRange = resolveEffectiveRange()
|
||||||
activePreset && activePreset !== 'all' ? computePresetRange(activePreset) : rangeRef.current
|
|
||||||
if (activePreset && activePreset !== 'all') {
|
|
||||||
setRange((current) =>
|
|
||||||
current && effectiveRange && current.startMs === effectiveRange.startMs && current.endMs === effectiveRange.endMs
|
|
||||||
? current
|
|
||||||
: effectiveRange,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const [nextBuckets, nextLogs, nextParams] = await Promise.all([
|
const [nextBuckets, nextLogs, nextParams] = await Promise.all([
|
||||||
fetchTimeline({
|
fetchTimeline({
|
||||||
@@ -436,8 +383,10 @@ function FullApp() {
|
|||||||
const [logs, setLogs] = useState<LogEntry[]>([])
|
const [logs, setLogs] = useState<LogEntry[]>([])
|
||||||
const [params, setParams] = useState<ParamDeclaration[]>([])
|
const [params, setParams] = useState<ParamDeclaration[]>([])
|
||||||
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
|
const [buckets, setBuckets] = useState<TimelineBucket[]>([])
|
||||||
const [range, setRange] = useState<TimeRange | null>(null)
|
const { range, activePreset, selectPreset, handleManualRangeSelect, resolveEffectiveRange } = useRangeSelection({
|
||||||
const [activePreset, setActivePreset] = useState<TimeRangePresetId | null>('all')
|
preset: 'all',
|
||||||
|
range: null,
|
||||||
|
})
|
||||||
const [filter, setFilter] = useState('')
|
const [filter, setFilter] = useState('')
|
||||||
const [filterError, setFilterError] = useState<string | null>(null)
|
const [filterError, setFilterError] = useState<string | null>(null)
|
||||||
const [logViewerCollapsed, setLogViewerCollapsed] = useSectionCollapsed('log-viewer')
|
const [logViewerCollapsed, setLogViewerCollapsed] = useSectionCollapsed('log-viewer')
|
||||||
@@ -461,12 +410,6 @@ function FullApp() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read via ref inside the poll loop instead of depending on `range` directly, which would restart the interval every rolling-preset tick.
|
|
||||||
const rangeRef = useRef(range)
|
|
||||||
useEffect(() => {
|
|
||||||
rangeRef.current = range
|
|
||||||
}, [range])
|
|
||||||
|
|
||||||
async function loadParams() {
|
async function loadParams() {
|
||||||
try {
|
try {
|
||||||
setParams(await fetchParamDeclarations())
|
setParams(await fetchParamDeclarations())
|
||||||
@@ -479,35 +422,12 @@ function FullApp() {
|
|||||||
loadParams()
|
loadParams()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
function selectPreset(id: TimeRangePresetId) {
|
|
||||||
setActivePreset(id)
|
|
||||||
setRange(computePresetRange(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A drag always yields a fixed (non-rolling) range, overriding any active preset.
|
|
||||||
function handleManualRangeSelect(next: TimeRange | null) {
|
|
||||||
if (next === null) {
|
|
||||||
selectPreset('all')
|
|
||||||
} else {
|
|
||||||
setActivePreset(null)
|
|
||||||
setRange(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
async function poll() {
|
async function poll() {
|
||||||
// Rolling presets re-resolve on this same polling cadence instead of freezing the window at selection time.
|
// Rolling presets re-resolve on this same polling cadence instead of freezing the window at selection time.
|
||||||
const effectiveRange =
|
const effectiveRange = resolveEffectiveRange()
|
||||||
activePreset && activePreset !== 'all' ? computePresetRange(activePreset) : rangeRef.current
|
|
||||||
if (activePreset && activePreset !== 'all') {
|
|
||||||
setRange((current) =>
|
|
||||||
current && effectiveRange && current.startMs === effectiveRange.startMs && current.endMs === effectiveRange.endMs
|
|
||||||
? current
|
|
||||||
: effectiveRange,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const [nextStats, nextBuckets, nextLogs, nextParams] = await Promise.all([
|
const [nextStats, nextBuckets, nextLogs, nextParams] = await Promise.all([
|
||||||
fetchStats(),
|
fetchStats(),
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { computePresetRange, type TimeRange, type TimeRangePresetId } from './timeRange'
|
||||||
|
|
||||||
|
// `resolveEffectiveRange` is meant to be called once at the top of each variant's own poll
|
||||||
|
// callback: it re-resolves a rolling preset to "now" on every tick (instead of freezing the
|
||||||
|
// window at selection time) while reading a manually-selected fixed range via a ref, so the poll
|
||||||
|
// loop's own `useEffect` doesn't need `range` itself as a dependency and restart its interval on
|
||||||
|
// every tick.
|
||||||
|
export function useRangeSelection(initial: { preset: TimeRangePresetId | null; range: TimeRange | null }) {
|
||||||
|
const [range, setRange] = useState<TimeRange | null>(
|
||||||
|
initial.preset ? computePresetRange(initial.preset) : initial.range,
|
||||||
|
)
|
||||||
|
const [activePreset, setActivePreset] = useState<TimeRangePresetId | null>(initial.preset)
|
||||||
|
|
||||||
|
const rangeRef = useRef(range)
|
||||||
|
useEffect(() => {
|
||||||
|
rangeRef.current = range
|
||||||
|
}, [range])
|
||||||
|
|
||||||
|
function selectPreset(id: TimeRangePresetId) {
|
||||||
|
setActivePreset(id)
|
||||||
|
setRange(computePresetRange(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A drag always yields a fixed (non-rolling) range, overriding any active preset.
|
||||||
|
function handleManualRangeSelect(next: TimeRange | null) {
|
||||||
|
if (next === null) {
|
||||||
|
selectPreset('all')
|
||||||
|
} else {
|
||||||
|
setActivePreset(null)
|
||||||
|
setRange(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveEffectiveRange(): TimeRange | null {
|
||||||
|
const effectiveRange = activePreset && activePreset !== 'all' ? computePresetRange(activePreset) : rangeRef.current
|
||||||
|
if (activePreset && activePreset !== 'all') {
|
||||||
|
setRange((current) =>
|
||||||
|
current && effectiveRange && current.startMs === effectiveRange.startMs && current.endMs === effectiveRange.endMs
|
||||||
|
? current
|
||||||
|
: effectiveRange,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return effectiveRange
|
||||||
|
}
|
||||||
|
|
||||||
|
return { range, activePreset, selectPreset, handleManualRangeSelect, resolveEffectiveRange }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user