ft build stream: auto scroll to latest log

This commit is contained in:
Bobby
2021-06-18 19:23:56 +10:00
parent 48dcbce547
commit 80bfd05be4
3 changed files with 84 additions and 5 deletions

View File

@@ -49,6 +49,7 @@
"react-router-dom": "^5.0.1",
"react-scripts": "^3.4.3",
"react-scroll-sync": "^0.8.0",
"react-usestateref": "^1.0.5",
"serve": "^11.3.2",
"tinymce": "^5.2.0",
"typescript": "^3.7.2",

View File

@@ -1,12 +1,14 @@
import React, { useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import useRouter from "hooks/useRouter";
import useTable from "hooks/useFiretable/useTable";
import { useFiretableContext } from "contexts/FiretableContext";
import useStateRef from "react-usestateref";
import _camelCase from "lodash/camelCase";
import _get from "lodash/get";
import _find from "lodash/find";
import _sortBy from "lodash/sortBy";
import _throttle from "lodash/throttle";
import moment from "moment";
import {
@@ -57,6 +59,10 @@ const useStyles = makeStyles((theme) => ({
width: "100%",
backgroundColor: "#1E1E1E",
},
logPanelProgress: {
marginLeft: "3em",
marginTop: "1em",
},
logEntryWrapper: {
overflowY: "scroll",
maxHeight: "100%",
@@ -136,23 +142,88 @@ function LogRow({ logRecord, index }) {
}
function LogPanel(props) {
const { logs, status, value, index, ...other } = props;
const { logs, status, value, index, isOpen, ...other } = props;
const classes = useStyles();
// useStateRef is necessary to resolve the state syncing issue
// https://stackoverflow.com/a/63039797/12208834
const [liveStreaming, setLiveStreaming, liveStreamingStateRef] = useStateRef(
true
);
const liveStreamingRef = useRef<any>();
const isActive = value === index;
const handleScroll = _throttle(() => {
const target = document.querySelector("#live-stream-target");
const scrollBox = document.querySelector("#live-stream-scroll-box");
const liveStreamTargetVisible = isTargetInsideBox(target, scrollBox);
if (liveStreamTargetVisible !== liveStreamingStateRef.current) {
console.log("live streaming:", liveStreamTargetVisible);
setLiveStreaming(liveStreamTargetVisible);
}
}, 100);
const scrollToLive = () => {
const liveStreamTarget = document.querySelector("#live-stream-target");
liveStreamTarget?.scrollIntoView?.({
behavior: "smooth",
});
};
const isTargetInsideBox = (target, box) => {
const targetRect = target.getBoundingClientRect();
const boxRect = box.getBoundingClientRect();
return targetRect.y < boxRect.y + boxRect.height;
};
useEffect(() => {
if (liveStreaming && isActive && status === "BUILDING") {
if (!liveStreamingRef.current) {
scrollToLive();
} else {
setTimeout(scrollToLive, 100);
}
}
}, [logs, value]);
useEffect(() => {
if (isActive) {
const liveStreamScrollBox = document.querySelector(
"#live-stream-scroll-box"
);
liveStreamScrollBox!.addEventListener("scroll", () => {
handleScroll();
});
}
}, [value]);
return (
<div
role="tabpanel"
hidden={value !== index}
hidden={!isActive}
id={`vertical-tabpanel-${index}`}
aria-labelledby={`vertical-tab-${index}`}
className={classes.logPanel}
{...other}
>
{value === index && (
<Box p={3} className={classes.logEntryWrapper}>
<Box
p={3}
className={classes.logEntryWrapper}
id="live-stream-scroll-box"
>
{logs?.map((log, index) => {
return <LogRow logRecord={log} index={index} />;
return <LogRow logRecord={log} index={index} key={index} />;
})}
<div ref={liveStreamingRef} id="live-stream-target">
{status === "BUILDING" && (
<CircularProgress
className={classes.logPanelProgress}
size={30}
/>
)}
</div>
<div style={{ height: 10 }} />
</Box>
)}
</div>
@@ -238,6 +309,7 @@ export default function TableLogs() {
>
{collectionState.rows.map((logEntry, index) => (
<Tab
key={index}
label={
<Box className={classes.tab}>
<Box>
@@ -260,6 +332,7 @@ export default function TableLogs() {
</Tabs>
{collectionState.rows.map((logEntry, index) => (
<LogPanel
key={index}
value={tabIndex}
index={index}
logs={logEntry?.fullLog}

View File

@@ -12685,6 +12685,11 @@ react-transition-group@^4.0.0, react-transition-group@^4.4.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-usestateref@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/react-usestateref/-/react-usestateref-1.0.5.tgz#0b5659217022a7e88087875ec1745730940b7882"
integrity sha512-Bia+nJDuhmakaUQR6ztVPvZnGz2cV8ZOh9Tlgpc2iZorwc2Li4xcQUICtMwdAms5N9bH8FnPX+mXn91EsExvvg==
"react@^16.8.0 || ^17.0.0":
version "17.0.1"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"