fix(events): load older logs on terminal scroll
This commit is contained in:
@@ -36,6 +36,7 @@ export function EventsPage() {
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [lastRefreshAt, setLastRefreshAt] = useState<string | null>(null);
|
||||
const sentinelRef = useRef<HTMLDivElement | null>(null);
|
||||
const feedRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const refreshFirstPage = useCallback(async (replace = false) => {
|
||||
if (replace) setLoading(true);
|
||||
@@ -74,19 +75,12 @@ export function EventsPage() {
|
||||
}
|
||||
}, [loadingMore, nextPageUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
const node = sentinelRef.current;
|
||||
if (!node || !nextPageUrl) return undefined;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries.some((entry) => entry.isIntersecting)) void loadMore();
|
||||
},
|
||||
{ rootMargin: "280px 0px" }
|
||||
);
|
||||
observer.observe(node);
|
||||
return () => observer.disconnect();
|
||||
}, [loadMore, nextPageUrl]);
|
||||
const handleFeedScroll = useCallback(() => {
|
||||
const node = feedRef.current;
|
||||
if (!node || !nextPageUrl || loadingMore) return;
|
||||
const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight;
|
||||
if (distanceFromBottom <= 140) void loadMore();
|
||||
}, [loadMore, loadingMore, nextPageUrl]);
|
||||
|
||||
const newestFirst = useMemo(() => [...events].sort((a, b) => b.id - a.id), [events]);
|
||||
|
||||
@@ -113,7 +107,7 @@ export function EventsPage() {
|
||||
<code>job-events.log</code>
|
||||
<span>refreshed {lastRefreshAt ? new Date(lastRefreshAt).toLocaleTimeString() : "-"}</span>
|
||||
</div>
|
||||
<div className="terminal-feed" role="log" aria-live="polite">
|
||||
<div className="terminal-feed" role="log" aria-live="polite" onScroll={handleFeedScroll} ref={feedRef}>
|
||||
{newestFirst.map((event) => (
|
||||
<article className={`terminal-line ${eventTone(event.type)}`} key={event.id}>
|
||||
<time className="terminal-time" dateTime={event.created_at}>
|
||||
@@ -131,15 +125,15 @@ export function EventsPage() {
|
||||
))}
|
||||
{loading && !newestFirst.length && <EmptyState title="Loading events" />}
|
||||
{!loading && !newestFirst.length && <EmptyState title="No events yet" />}
|
||||
</div>
|
||||
<div className="infinite-sentinel" ref={sentinelRef}>
|
||||
{loadingMore && <span>Loading older events...</span>}
|
||||
{!loadingMore && nextPageUrl && (
|
||||
<button className="secondary-button" type="button" onClick={() => void loadMore()}>
|
||||
Load older events
|
||||
</button>
|
||||
)}
|
||||
{!loadingMore && !nextPageUrl && newestFirst.length > 0 && <span>End of event history</span>}
|
||||
<div className="infinite-sentinel" ref={sentinelRef}>
|
||||
{loadingMore && <span>Loading older events...</span>}
|
||||
{!loadingMore && nextPageUrl && (
|
||||
<button className="secondary-button" type="button" onClick={() => void loadMore()}>
|
||||
Load older events
|
||||
</button>
|
||||
)}
|
||||
{!loadingMore && !nextPageUrl && newestFirst.length > 0 && <span>End of event history</span>}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
--panel-raised: #f9fbfc;
|
||||
--background: #f4f7f9;
|
||||
--shadow: 0 18px 46px rgb(35 36 38 / 8%);
|
||||
--terminal-scroll-thumb: #0bb3f0;
|
||||
--terminal-scroll-thumb-hover: #f0bb0b;
|
||||
--terminal-scroll-track: #171b1f;
|
||||
color: var(--ink);
|
||||
background: var(--background);
|
||||
font-synthesis: none;
|
||||
@@ -60,6 +63,9 @@
|
||||
--panel-raised: #25282b;
|
||||
--background: #141618;
|
||||
--shadow: 0 18px 46px rgb(0 0 0 / 28%);
|
||||
--terminal-scroll-thumb: #f0bb0b;
|
||||
--terminal-scroll-thumb-hover: #55c8ff;
|
||||
--terminal-scroll-track: #101316;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -1250,6 +1256,31 @@ label {
|
||||
min-height: 420px;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
scrollbar-color: var(--terminal-scroll-thumb) var(--terminal-scroll-track);
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.terminal-feed::-webkit-scrollbar {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.terminal-feed::-webkit-scrollbar-track {
|
||||
background: var(--terminal-scroll-track);
|
||||
}
|
||||
|
||||
.terminal-feed::-webkit-scrollbar-thumb {
|
||||
background: var(--terminal-scroll-thumb);
|
||||
border: 2px solid var(--terminal-scroll-track);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.terminal-feed::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--terminal-scroll-thumb-hover);
|
||||
}
|
||||
|
||||
.terminal-feed::-webkit-scrollbar-corner {
|
||||
background: var(--terminal-scroll-track);
|
||||
}
|
||||
|
||||
.terminal-line {
|
||||
|
||||
Reference in New Issue
Block a user