Skip to content

Commit 3d5bd70

Browse files
adityathebemoshloop
authored andcommitted
fix(config): reset live-tailed changes on query changes
Config changes live-tail mode keeps a local buffer of newly polled rows. That buffer was merged into the table even after filters, sorting, pagination, or date params changed, so stale rows from the previous query remained visible. Only merge live-tailed rows while Live mode is enabled, and clear the live-tail cursor and buffer when query params change.
1 parent df19980 commit 3d5bd70

2 files changed

Lines changed: 76 additions & 15 deletions

File tree

src/pages/config/ConfigChangesPage.tsx

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { SearchLayout } from "@flanksource-ui/ui/Layout/SearchLayout";
1515
import { refreshButtonClickedTrigger } from "@flanksource-ui/ui/SlidingSideBar/SlidingSideBar";
1616
import { Toggle } from "@flanksource-ui/ui/FormControls/Toggle";
1717
import { useAtom } from "jotai";
18-
import { useEffect, useState } from "react";
18+
import { useEffect, useMemo, useRef, useState } from "react";
1919
import { useSearchParams } from "react-router-dom";
2020

2121
function getNewestInsertedAt(changes: ConfigChange[]): string | undefined {
@@ -29,6 +29,16 @@ function getNewestInsertedAt(changes: ConfigChange[]): string | undefined {
2929
return latest;
3030
}
3131

32+
function getLiveTailQueryKey(params: URLSearchParams) {
33+
return Array.from(params.entries())
34+
.filter(([key]) => key !== "changeId")
35+
.sort(([aKey, aValue], [bKey, bValue]) =>
36+
aKey === bKey ? aValue.localeCompare(bValue) : aKey.localeCompare(bKey)
37+
)
38+
.map(([key, value]) => `${key}=${value}`)
39+
.join("&");
40+
}
41+
3242
export function ConfigChangesPage() {
3343
const [, setRefreshButtonClickedTrigger] = useAtom(
3444
refreshButtonClickedTrigger
@@ -48,18 +58,20 @@ export function ConfigChangesPage() {
4858
const [liveTail, setLiveTail] = useState(false);
4959
const [tailCursor, setTailCursor] = useState<string | undefined>(undefined);
5060
const [tailedChanges, setTailedChanges] = useState<ConfigChange[]>([]);
61+
const liveTailQueryKey = useMemo(() => getLiveTailQueryKey(params), [params]);
62+
const liveTailQueryKeyRef = useRef(liveTailQueryKey);
5163

52-
const { data, isLoading, error, isRefetching, refetch } =
64+
const { data, isLoading, error, isRefetching, refetch, isPreviousData } =
5365
useGetAllConfigsChangesQuery({
5466
keepPreviousData: true
5567
});
5668

5769
// Initialize cursor from base data when live tail is turned on
5870
useEffect(() => {
59-
if (liveTail && data?.changes?.length && !tailCursor) {
71+
if (liveTail && data?.changes?.length && !tailCursor && !isPreviousData) {
6072
setTailCursor(getNewestInsertedAt(data.changes));
6173
}
62-
}, [liveTail, data, tailCursor]);
74+
}, [liveTail, data, tailCursor, isPreviousData]);
6375

6476
// Reset when live tail is turned off
6577
useEffect(() => {
@@ -70,6 +82,20 @@ export function ConfigChangesPage() {
7082
}
7183
}, [liveTail, refetch]);
7284

85+
// Reset live tail state whenever filters, sorting, pagination, or date range changes.
86+
useEffect(() => {
87+
if (!liveTail) {
88+
liveTailQueryKeyRef.current = liveTailQueryKey;
89+
return;
90+
}
91+
if (liveTailQueryKeyRef.current === liveTailQueryKey) {
92+
return;
93+
}
94+
liveTailQueryKeyRef.current = liveTailQueryKey;
95+
setTailedChanges([]);
96+
setTailCursor(undefined);
97+
}, [liveTail, liveTailQueryKey]);
98+
7399
const { data: pollData } = useGetAllConfigsChangesQuery({
74100
from_inserted_at: tailCursor,
75101
keepPreviousData: false,
@@ -120,9 +146,13 @@ export function ConfigChangesPage() {
120146
const newTailedCount = tailedWithConfig.filter(
121147
(c) => !baseIds.has(c.id)
122148
).length;
123-
const changes = [...tailedWithConfig, ...baseWithoutTailed];
149+
const changes = liveTail
150+
? [...tailedWithConfig, ...baseWithoutTailed]
151+
: baseChanges;
124152

125-
const totalChanges = (data?.total ?? 0) + newTailedCount;
153+
const totalChanges = liveTail
154+
? (data?.total ?? 0) + newTailedCount
155+
: (data?.total ?? 0);
126156
const totalChangesPages = Math.ceil(totalChanges / parseInt(pageSize));
127157

128158
const errorMessage =

src/pages/config/details/ConfigDetailsChangesPage.tsx

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ConfigRelatedChangesFilters } from "@flanksource-ui/components/Configs/
55
import { ConfigDetailsTabs } from "@flanksource-ui/components/Configs/ConfigDetailsTabs";
66
import { InfoMessage } from "@flanksource-ui/components/InfoMessage";
77
import { Toggle } from "@flanksource-ui/ui/FormControls/Toggle";
8-
import { useEffect, useState } from "react";
8+
import { useEffect, useMemo, useRef, useState } from "react";
99
import { useParams, useSearchParams } from "react-router-dom";
1010

1111
function getNewestInsertedAt(changes: ConfigChange[]): string | undefined {
@@ -19,6 +19,16 @@ function getNewestInsertedAt(changes: ConfigChange[]): string | undefined {
1919
return latest;
2020
}
2121

22+
function getLiveTailQueryKey(params: URLSearchParams) {
23+
return Array.from(params.entries())
24+
.filter(([key]) => key !== "changeId")
25+
.sort(([aKey, aValue], [bKey, bValue]) =>
26+
aKey === bKey ? aValue.localeCompare(bValue) : aKey.localeCompare(bKey)
27+
)
28+
.map(([key, value]) => `${key}=${value}`)
29+
.join("&");
30+
}
31+
2232
export function ConfigDetailsChangesPage() {
2333
const { id } = useParams();
2434

@@ -32,18 +42,21 @@ export function ConfigDetailsChangesPage() {
3242
const [liveTail, setLiveTail] = useState(false);
3343
const [tailCursor, setTailCursor] = useState<string | undefined>(undefined);
3444
const [tailedChanges, setTailedChanges] = useState<ConfigChange[]>([]);
45+
const liveTailQueryKey = useMemo(() => getLiveTailQueryKey(params), [params]);
46+
const liveTailQueryKeyRef = useRef(liveTailQueryKey);
3547

36-
const { data, isLoading, error, refetch } = useGetConfigChangesByIDQuery({
37-
keepPreviousData: true,
38-
enabled: !!id
39-
});
48+
const { data, isLoading, error, refetch, isPreviousData } =
49+
useGetConfigChangesByIDQuery({
50+
keepPreviousData: true,
51+
enabled: !!id
52+
});
4053

4154
// Initialize cursor from base data when live tail is turned on
4255
useEffect(() => {
43-
if (liveTail && data?.changes?.length && !tailCursor) {
56+
if (liveTail && data?.changes?.length && !tailCursor && !isPreviousData) {
4457
setTailCursor(getNewestInsertedAt(data.changes));
4558
}
46-
}, [liveTail, data, tailCursor]);
59+
}, [liveTail, data, tailCursor, isPreviousData]);
4760

4861
// Reset when live tail is turned off
4962
useEffect(() => {
@@ -54,6 +67,20 @@ export function ConfigDetailsChangesPage() {
5467
}
5568
}, [liveTail, refetch]);
5669

70+
// Reset live tail state whenever filters, sorting, pagination, or date range changes.
71+
useEffect(() => {
72+
if (!liveTail) {
73+
liveTailQueryKeyRef.current = liveTailQueryKey;
74+
return;
75+
}
76+
if (liveTailQueryKeyRef.current === liveTailQueryKey) {
77+
return;
78+
}
79+
liveTailQueryKeyRef.current = liveTailQueryKey;
80+
setTailedChanges([]);
81+
setTailCursor(undefined);
82+
}, [liveTail, liveTailQueryKey]);
83+
5784
const { data: pollData } = useGetConfigChangesByIDQuery({
5885
from_inserted_at: tailCursor,
5986
keepPreviousData: false,
@@ -102,9 +129,13 @@ export function ConfigDetailsChangesPage() {
102129
const newTailedCount = tailedWithConfig.filter(
103130
(c) => !baseIds.has(c.id)
104131
).length;
105-
const changes = [...tailedWithConfig, ...baseWithoutTailed];
132+
const changes = liveTail
133+
? [...tailedWithConfig, ...baseWithoutTailed]
134+
: baseChanges;
106135

107-
const totalChanges = (data?.total ?? 0) + newTailedCount;
136+
const totalChanges = liveTail
137+
? (data?.total ?? 0) + newTailedCount
138+
: (data?.total ?? 0);
108139
const totalChangesPages = Math.ceil(totalChanges / parseInt(pageSize));
109140

110141
if (error) {

0 commit comments

Comments
 (0)