Skip to content

Commit 2fa724c

Browse files
author
Dylan Huang
committed
Enhance LogsSection component with improved log change detection and auto-scroll behavior
- Introduced a function to detect changes in logs and conditionally update the state. - Added refs to manage scroll position and determine if the user is at the bottom of the logs. - Updated auto-scroll logic to ensure smooth scrolling when new logs arrive. - Minor UI adjustments for better layout and readability.
1 parent 270a91e commit 2fa724c

File tree

1 file changed

+56
-6
lines changed

1 file changed

+56
-6
lines changed

vite-app/src/components/LogsSection.tsx

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { getApiUrl } from "../config";
99
import Select from "./Select";
1010
import Button from "./Button";
1111

12+
const haveLogsChanged = (prevLogs: LogEntry[], nextLogs: LogEntry[]) => {
13+
return prevLogs.length !== nextLogs.length;
14+
};
15+
1216
interface LogsSectionProps {
1317
rolloutId?: string;
1418
}
@@ -19,6 +23,8 @@ export const LogsSection = observer(({ rolloutId }: LogsSectionProps) => {
1923
const [error, setError] = useState<string | null>(null);
2024
const [selectedLevel, setSelectedLevel] = useState<string>("");
2125
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
26+
const isAtBottomRef = useRef(true);
27+
const shouldAutoScrollRef = useRef(false);
2228

2329
const fetchLogs = async (isInitialLoad = false) => {
2430
if (!rolloutId) return;
@@ -93,7 +99,19 @@ export const LogsSection = observer(({ rolloutId }: LogsSectionProps) => {
9399
const data: LogsResponse = LogsResponseSchema.parse(
94100
await response.json()
95101
);
96-
setLogs(data.logs);
102+
setLogs((prevLogs) => {
103+
const hasChanges = haveLogsChanged(prevLogs, data.logs);
104+
105+
if (!hasChanges) {
106+
return prevLogs;
107+
}
108+
109+
if (isAtBottomRef.current) {
110+
shouldAutoScrollRef.current = true;
111+
}
112+
113+
return data.logs;
114+
});
97115
} catch (err) {
98116
if (err instanceof Error && err.message.includes("Unexpected token")) {
99117
setError(
@@ -115,11 +133,43 @@ export const LogsSection = observer(({ rolloutId }: LogsSectionProps) => {
115133
}
116134
}, [rolloutId, selectedLevel]);
117135

118-
// Auto-scroll to bottom whenever logs update
119136
useEffect(() => {
120137
const el = scrollContainerRef.current;
121-
if (!el) return;
138+
139+
if (!el) {
140+
isAtBottomRef.current = true;
141+
return;
142+
}
143+
144+
const handleScroll = () => {
145+
const distanceFromBottom =
146+
el.scrollHeight - el.scrollTop - el.clientHeight;
147+
isAtBottomRef.current = distanceFromBottom <= 8;
148+
};
149+
150+
el.addEventListener("scroll", handleScroll);
151+
handleScroll();
152+
153+
return () => {
154+
el.removeEventListener("scroll", handleScroll);
155+
};
156+
}, [logs.length]);
157+
158+
// Auto-scroll to bottom when new logs arrive and user was already at bottom
159+
useEffect(() => {
160+
if (!shouldAutoScrollRef.current) {
161+
return;
162+
}
163+
164+
const el = scrollContainerRef.current;
165+
if (!el) {
166+
shouldAutoScrollRef.current = false;
167+
return;
168+
}
169+
122170
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
171+
shouldAutoScrollRef.current = false;
172+
isAtBottomRef.current = true;
123173
}, [logs]);
124174

125175
if (!rolloutId) {
@@ -170,13 +220,13 @@ export const LogsSection = observer(({ rolloutId }: LogsSectionProps) => {
170220
{logs.length > 0 && (
171221
<div
172222
ref={scrollContainerRef}
173-
className="max-h-[800px] min-h-4 overflow-auto border border-gray-200"
223+
className="max-h-[800px] min-h-4 overflow-auto border border-gray-200 bg-white"
174224
>
175-
<div>
225+
<div className="min-w-max">
176226
{logs.map((log, index) => (
177227
<div
178228
key={index}
179-
className={`text-xs px-3 py-1 border-b border-gray-200 last:border-b-0 ${
229+
className={`w-full text-xs px-3 py-1 border-b border-gray-200 last:border-b-0 ${
180230
index % 2 === 0 ? "bg-white" : "bg-gray-50"
181231
}`}
182232
>

0 commit comments

Comments
 (0)