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}
)
}