mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
185 lines
5.5 KiB
TypeScript
185 lines
5.5 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import { throttle } from "lodash-es";
|
|
import { useSetAtom } from "jotai";
|
|
|
|
import { Typography, Box, Tooltip, IconButton } from "@mui/material";
|
|
import ExpandIcon from "@mui/icons-material/ExpandLess";
|
|
import CollapseIcon from "@mui/icons-material/ExpandMore";
|
|
import OpenIcon from "@mui/icons-material/Fullscreen";
|
|
import CloseIcon from "@mui/icons-material/Close";
|
|
|
|
import BuildLogRow from "./BuildLogRow";
|
|
import CircularProgressOptical from "@src/components/CircularProgressOptical";
|
|
|
|
import { isTargetInsideBox } from "@src/utils/ui";
|
|
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
|
|
import useBuildLogs from "./useBuildLogs";
|
|
import {
|
|
tableScope,
|
|
tableModalAtom,
|
|
cloudLogFiltersAtom,
|
|
} from "@src/atoms/tableScope";
|
|
|
|
export interface IBuildLogsSnackProps {
|
|
onClose: () => void;
|
|
onOpenPanel: () => void;
|
|
}
|
|
|
|
export default function BuildLogsSnack({
|
|
onClose,
|
|
onOpenPanel,
|
|
}: IBuildLogsSnackProps) {
|
|
const snackLogContext = useSnackLogContext();
|
|
const { latestLog } = useBuildLogs();
|
|
const setModal = useSetAtom(tableModalAtom, tableScope);
|
|
const setCloudLogFilters = useSetAtom(cloudLogFiltersAtom, tableScope);
|
|
const latestActiveLog =
|
|
latestLog?.startTimeStamp > snackLogContext.latestBuildTimestamp - 5000 ||
|
|
latestLog?.startTimeStamp > snackLogContext.latestBuildTimestamp + 5000
|
|
? latestLog
|
|
: null;
|
|
const logs = latestActiveLog?.fullLog;
|
|
const status = latestActiveLog?.status;
|
|
|
|
const [expanded, setExpanded] = useState(false);
|
|
const [liveStreaming, setLiveStreaming] = useState(true);
|
|
const liveStreamingRef = useRef<any>();
|
|
|
|
const handleScroll = throttle(() => {
|
|
const target = document.querySelector("#live-stream-target-snack");
|
|
const scrollBox = document.querySelector("#live-stream-scroll-box-snack");
|
|
const liveStreamTargetVisible =
|
|
target && scrollBox && isTargetInsideBox(target, scrollBox);
|
|
setLiveStreaming(Boolean(liveStreamTargetVisible));
|
|
}, 100);
|
|
|
|
const scrollToLive = () => {
|
|
const liveStreamTarget = document.querySelector(
|
|
"#live-stream-target-snack"
|
|
);
|
|
liveStreamTarget?.scrollIntoView?.();
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (liveStreaming && status === "BUILDING") {
|
|
if (!liveStreamingRef.current) {
|
|
scrollToLive();
|
|
} else {
|
|
setTimeout(scrollToLive, 500);
|
|
}
|
|
}
|
|
}, [latestActiveLog]);
|
|
|
|
useEffect(() => {
|
|
const liveStreamScrollBox = document.querySelector(
|
|
"#live-stream-scroll-box-snack"
|
|
);
|
|
liveStreamScrollBox!.addEventListener("scroll", () => {
|
|
handleScroll();
|
|
});
|
|
}, []);
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
position: "absolute",
|
|
left: (theme) => theme.spacing(2),
|
|
bottom: (theme) => theme.spacing(2),
|
|
backgroundColor: "#282829",
|
|
boxShadow: 6,
|
|
color: "#fff",
|
|
width: 650,
|
|
p: 2,
|
|
pt: 1,
|
|
borderRadius: 1,
|
|
zIndex: 1,
|
|
transition: (theme) => theme.transitions.create("height"),
|
|
height: expanded ? "calc(100% - 300px)" : 50,
|
|
}}
|
|
>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|
<Typography
|
|
variant="subtitle2"
|
|
color={
|
|
latestActiveLog?.status === "SUCCESS"
|
|
? "success.light"
|
|
: latestActiveLog?.status === "FAIL"
|
|
? "error.light"
|
|
: ""
|
|
}
|
|
>
|
|
{!latestActiveLog && "Build pending…"}
|
|
{latestActiveLog?.status === "SUCCESS" && "Build success"}
|
|
{latestActiveLog?.status === "FAIL" && "Build failed"}
|
|
{latestActiveLog?.status === "BUILDING" && "Building…"}
|
|
</Typography>
|
|
|
|
<Box>
|
|
<Tooltip title="Expand">
|
|
<IconButton
|
|
aria-label="Expand"
|
|
size="small"
|
|
onClick={() => setExpanded(!expanded)}
|
|
style={{ color: "white" }}
|
|
>
|
|
{expanded ? <CollapseIcon /> : <ExpandIcon />}
|
|
</IconButton>
|
|
</Tooltip>
|
|
|
|
<Tooltip title="Full screen">
|
|
<IconButton
|
|
aria-label="Full screen"
|
|
size="small"
|
|
onClick={() => {
|
|
setModal("cloudLogs");
|
|
setCloudLogFilters({
|
|
type: "build",
|
|
timeRange: { type: "days", value: 7 },
|
|
buildLogExpanded: 0,
|
|
});
|
|
}}
|
|
style={{ color: "white" }}
|
|
>
|
|
<OpenIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
|
|
<Tooltip title="Close">
|
|
<IconButton
|
|
aria-label="Close"
|
|
size="small"
|
|
onClick={onClose}
|
|
style={{ color: "white" }}
|
|
>
|
|
<CloseIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Box
|
|
sx={{
|
|
overflowY: "scroll",
|
|
maxHeight: "100%",
|
|
}}
|
|
height={"calc(100% - 25px)"}
|
|
id="live-stream-scroll-box-snack"
|
|
>
|
|
{latestActiveLog && expanded && (
|
|
<>
|
|
{logs?.map((log: any, index: number) => (
|
|
<BuildLogRow logRecord={log} index={index} key={index} />
|
|
))}
|
|
<div ref={liveStreamingRef} id="live-stream-target-snack">
|
|
{status === "BUILDING" && (
|
|
<CircularProgressOptical sx={{ ml: 4, mt: 2 }} size={30} />
|
|
)}
|
|
</div>
|
|
<div style={{ height: 10 }} />
|
|
</>
|
|
)}
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|