Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/compute/doc-status.tsx
Views: 687
/*1This is a component that should be placed at the top of a document to help2the user when they have requested their document run on a given compute3server. It does the following:45- If id is as requested and is the project, do nothing.67- If id is as requested and is not the project, draw line in color of that compute server.89- If not where we want to be, defines how close via a percentage1011- If compute server not running:12- if exists and you own it, prompts user to start it and also shows the13compute server's component so they can do so.14- if not exists (or deleted), say so15- if owned by somebody else, say so16*/1718import Inline from "./inline";19import { useTypedRedux } from "@cocalc/frontend/app-framework";20import { Alert, Button, Progress, Space, Spin, Tooltip } from "antd";21import type { ComputeServerUserInfo } from "@cocalc/util/db-schema/compute-servers";22import ComputeServer from "./compute-server";23import { useEffect, useState } from "react";24import { Icon } from "@cocalc/frontend/components";25import SyncButton from "./sync-button";26import { avatar_fontcolor } from "@cocalc/frontend/account/avatar/font-color";27import { DisplayImage } from "./select-image";28import Menu from "./menu";2930interface Props {31project_id: string;32id: number;33requestedId: number;34noSync?: boolean;35standalone?: boolean;36}3738export function ComputeServerDocStatus({39project_id,40id,41requestedId,42noSync,43standalone,44}: Props) {45const [showDetails, setShowDetails] = useState<boolean | null>(null);46const computeServers = useTypedRedux({ project_id }, "compute_servers");47const account_id = useTypedRedux("account", "account_id");4849useEffect(() => {50// if the id or requestedId changes, need to reset to default behavior51// regarding what is shown.52setShowDetails(null);53}, [id, requestedId]);5455const requestedServer = computeServers?.get(`${requestedId}`);56const syncExclude = requestedServer?.getIn([57"configuration",58"excludeFromSync",59]);60const excludeFromSync =61syncExclude?.includes("~") || syncExclude?.includes(".");62const syncState = requestedServer?.getIn([63"detailed_state",64"filesystem-sync",65]);6667// show sync errors68useEffect(() => {69if (syncState?.get("extra")) {70setShowDetails(true);71}72}, [syncState?.get("extra")]);7374if (id == 0 && requestedId == 0) {75return null;76}7778if (computeServers == null) {79return null;80}8182const topBar = (progress) => (83<div84style={{85display: "flex",86borderBottom:87!standalone && requestedServer != null && !showDetails88? "1px solid #ccc"89: undefined,90...(standalone91? { border: "1px solid #ddd", borderRadius: "5px" }92: undefined),93}}94>95{progress == 100 && !noSync && (96<SyncButton97type="text"98disabled={excludeFromSync}99style={{100marginLeft: "-3px",101float: "right",102width: "90px",103}}104size="small"105compute_server_id={id}106project_id={project_id}107time={syncState?.get("time")}108syncing={109requestedServer?.get("state") == "running" &&110!syncState?.get("extra") &&111(syncState?.get("progress") ?? 100) <11280 /* 80 because the last per for read cache is not sync and sometimes gets stuck */113}114>115Sync116</SyncButton>117)}118{progress < 100 && (119<Tooltip title={"Make sure the compute server is running."}>120<div121onClick={() => {122setShowDetails(showDetails === true ? false : true);123}}124style={{125whiteSpace: "nowrap",126padding: "2.5px 5px",127background: "darkred",128color: "white",129height: "24px",130}}131>132NOT CONNECTED133</div>134</Tooltip>135)}136<Tooltip137mouseEnterDelay={0.9}138title={139<>140{progress == 100 ? "Running on " : "Opening on "}{" "}141<Inline id={requestedId} computeServer={requestedServer} />.142</>143}144>145<div146onClick={() => {147setShowDetails(showDetails === true ? false : true);148}}149style={{150height: "24px",151cursor: "pointer",152padding: "2px 5px",153background: requestedServer?.get("color") ?? "#fff",154color: avatar_fontcolor(requestedServer?.get("color") ?? "#fff"),155width: "100%",156overflow: "hidden",157textAlign: "center",158}}159>160{progress < 100 ? `${progress}% - ` : ""}161<div style={{ display: "inline-block" }}>162<div style={{ display: "flex" }}>163<div164style={{165maxWidth: "30ex",166textOverflow: "ellipsis",167overflow: "hidden",168whiteSpace: "nowrap",169marginRight: "5px",170}}171>172{requestedServer?.get("title") ?? "Loading..."}173</div>174(Id: {requestedServer?.get("project_specific_id")})175</div>176</div>177<DisplayImage178style={{179marginLeft: "10px",180borderLeft: "1px solid black",181paddingLeft: "10px",182}}183configuration={requestedServer?.get("configuration")?.toJS()}184/>185</div>186</Tooltip>187<Menu188fontSize={"13pt"}189size="small"190style={{ marginTop: "1px", height: "10px" }}191id={requestedId}192project_id={project_id}193/>194</div>195);196197const server: ComputeServerUserInfo | undefined = computeServers198?.get(`${requestedId}`)199?.toJS();200const { progress, message, status } = getProgress(201server,202account_id,203id,204requestedId,205);206if (!showDetails) {207if (showDetails == null && progress < 100) {208setShowDetails(true);209}210return topBar(progress);211}212213return (214<div215className="smc-vfill"216style={{ flex: 3, minHeight: "300px", background: "white" }}217>218<div>{topBar(progress)}</div>219<div220className="smc-vfill"221style={{222border: `1px solid #ccc`,223borderRadius: "5px",224margin: "15px",225padding: "5px",226boxShadow: "rgba(33, 33, 33, 0.5) 1px 5px 7px",227marginTop: "0px",228overflow: "auto",229}}230>231<div232style={{233textAlign: "center",234}}235>236<Space style={{ width: "100%", margin: "15px 0" }}>237<Button238size="large"239type="text"240onClick={() => setShowDetails(false)}241>242<Icon name="times" /> Hide243</Button>244<Progress245type="circle"246trailColor="#e6f4ff"247percent={progress}248strokeWidth={14}249size={42}250/>251<Alert252style={{ margin: "0 15px" }}253type="info"254message={255<>256{message}{" "}257{progress < 100 && status != "exception" ? (258<Spin style={{ marginLeft: "15px" }} />259) : undefined}260</>261}262/>263</Space>264</div>265{server != null && (266<ComputeServer267editable={account_id == server.account_id}268server={server}269/>270)}271</div>272</div>273);274}275276function getProgress(277server: ComputeServerUserInfo | undefined,278account_id,279id,280requestedId,281): {282progress: number;283message: string;284status: "exception" | "active" | "normal" | "success";285} {286if (requestedId == 0) {287return {288progress: 50,289message: "Moving back to project...",290status: "active",291};292}293if (server == null) {294return {295progress: 0,296message: "Server does not exist. Please select a different server.",297status: "exception",298};299}300if (server.deleted) {301return {302progress: 0,303message:304"Server was deleted. Please select a different server or undelete it.",305status: "exception",306};307}308309if (310server.account_id != account_id &&311server.state != "running" &&312server.state != "starting"313) {314return {315progress: 0,316message:317"This is not your compute server, and it is not running. Only the owner of a compute server can start it.",318status: "exception",319};320}321322// below here it isn't our server, it is running.323324if (server.state == "deprovisioned") {325return {326progress: 0,327message:328"Please start the compute server by clicking the Start button below.",329status: "exception",330};331}332333if (server.state == "off") {334return {335progress: 10,336message:337"Please start the compute server by clicking the Start button below.",338status: "exception",339};340}341if (server.state == "suspended") {342return {343progress: 15,344message:345"Please resume the compute server by clicking the Resume button below.",346status: "exception",347};348}349350if (server.state != "starting" && server.state != "running") {351return {352progress: 25,353message: "Please start the compute server.",354status: "exception",355};356}357358if (server.state == "starting") {359return {360progress: 40,361message: "Compute server is starting.",362status: "active",363};364}365366// below it is running367368const computeIsLive = server.detailed_state?.compute?.state == "ready";369if (computeIsLive) {370if (id == requestedId) {371return {372progress: 100,373message: "Compute server is fully connected!",374status: "success",375};376} else {377return {378progress: 90,379message:380"Compute server is connected and should attach to this file soon...",381status: "success",382};383}384}385const filesystemIsLive =386server.detailed_state?.["filesystem-sync"]?.state == "ready";387const computeIsRecent = isRecent(server.detailed_state?.compute?.time);388const filesystemIsRecent = isRecent(389server.detailed_state?.["filesystem-sync"]?.time,390);391if (filesystemIsRecent) {392return {393progress: 70,394message: "Waiting for filesystem to connect.",395status: "normal",396};397}398if (filesystemIsLive) {399if (computeIsRecent) {400return {401progress: 80,402message: "Waiting for compute to connect.",403status: "normal",404};405}406}407408return {409progress: 50,410message:411"Compute server is running, but filesystem and compute components aren't connected. Waiting...",412status: "active",413};414}415416function isRecent(expire = 0) {417return Date.now() - expire < 60 * 1000;418}419420421