import { Badge, Button, Center, Checkbox, Divider, Flex, Grid, GridItem, Heading, HStack, Progress, ProgressIndicator, Spacer, Text, VStack, } from "@hope-ui/solid" import { createSignal, For, Show } from "solid-js" import { useT, useFetch } from "~/hooks" import { PEmptyResp } from "~/types" import { handleResp, notify, r } from "~/utils" import { TaskAttribute, TaskLocalSetter, TasksProps } from "./Tasks" import { me } from "~/store" enum TaskStateEnum { Pending, Running, Succeeded, Canceling, Canceled, Errored, Failing, Failed, WaitingRetry, BeforeRetry, } const StateMap: Record< string, | "primary" | "accent" | "neutral" | "success" | "info" | "warning" | "danger" | undefined > = { [TaskStateEnum.Failed]: "danger", [TaskStateEnum.Succeeded]: "success", [TaskStateEnum.Canceled]: "neutral", } const Creator = (props: { name: string; role: number }) => { if (props.role < 0) return null const roleColors = ["info", "neutral", "accent"] return ( {props.name} ) } export const TaskState = (props: { state: number }) => { const t = useT() return ( {t(`tasks.state.${props.state}`)} ) } export type TaskOrderBy = "name" | "creator" | "state" | "progress" export interface TaskCol { name: TaskOrderBy | "speed" | "operation" textAlign: "left" | "right" | "center" w: any } export const cols: TaskCol[] = [ { name: "name", textAlign: "left", w: me().role.includes(2) ? "calc(100% - 660px)" : "calc(100% - 560px)", }, { name: "creator", textAlign: "center", w: me().role.includes(2) ? "100px" : "0", }, { name: "state", textAlign: "center", w: "100px" }, { name: "progress", textAlign: "left", w: "140px" }, { name: "speed", textAlign: "center", w: "100px" }, { name: "operation", textAlign: "right", w: "220px" }, ] export interface TaskLocal { selected: boolean expanded: boolean } const toTimeNumber = (n: number) => { return Math.floor(n).toString().padStart(2, "0") } const getTimeStr = (millisecond: number) => { const sec = (millisecond / 1000) % 60 const min = (millisecond / 1000 / 60) % 60 const hour = millisecond / 1000 / 3600 return `${toTimeNumber(hour)}:${toTimeNumber(min)}:${toTimeNumber(sec)}` } export const Task = (props: TaskAttribute & TasksProps & TaskLocalSetter) => { const t = useT() const operateName = props.done === "undone" ? "cancel" : "delete" const canRetry = props.done === "done" && (props.state === TaskStateEnum.Failed || props.state === TaskStateEnum.Canceled) const [operateLoading, operate] = useFetch( (): PEmptyResp => r.post(`/task/${props.type}/${operateName}?tid=${props.id}`), ) const [retryLoading, retry] = useFetch( (): PEmptyResp => r.post(`/task/${props.type}/retry?tid=${props.id}`), ) const [deleted, setDeleted] = createSignal(false) const matches: RegExpMatchArray | null = props.name.match( props.nameAnalyzer.regex, ) const title = matches === null ? props.name : props.nameAnalyzer.title(matches, props) const startTime = props.start_time === null ? -1 : new Date(props.start_time).getTime() const endTime = props.end_time === null ? new Date().getTime() : new Date(props.end_time).getTime() let speedText = "-" const parseSpeedText = (timeDelta: number, lengthDelta: number) => { let delta = lengthDelta / timeDelta let unit = "bytes/s" if (delta > 1024) { delta /= 1024 unit = "KB/s" } if (delta > 1024) { delta /= 1024 unit = "MB/s" } if (delta > 1024) { delta /= 1024 unit = "GB/s" } return `${delta.toFixed(2)} ${unit}` } if (props.done) { if ( props.start_time !== props.end_time && props.progress > 0 && startTime !== -1 ) { const timeDelta = (endTime - startTime) / 1000 const lengthDelta = (props.total_bytes * props.progress) / 100 speedText = parseSpeedText(timeDelta, lengthDelta) } } else if ( props.prevProgress !== undefined && props.prevFetchTime !== undefined ) { const timeDelta = (props.curFetchTime - props.prevFetchTime) / 1000 const lengthDelta = ((props.progress - props.prevProgress) * props.total_bytes) / 100 speedText = parseSpeedText(timeDelta, lengthDelta) } return ( { e.stopPropagation() }} checked={props.local.selected} onChange={(e: any) => { props.setLocal({ selected: e.target.checked as boolean, expanded: props.local.expanded, }) }} /> {title}
{/* */}
{speedText}
{t(`tasks.attr.time_elapsed`)} {getTimeStr(endTime - startTime)} {(entry) => { const value = entry[1](matches as RegExpMatchArray, props) return value === undefined ? null : ( {entry[0]} {value} ) }} {t(`tasks.attr.status`)} {props.nameAnalyzer.statusText?.(props) ?? props.status} {t(`tasks.attr.err`)} {props.error}
) }