diff --git a/components/Task.tsx b/components/Task.tsx new file mode 100644 index 0000000..6cb7051 --- /dev/null +++ b/components/Task.tsx @@ -0,0 +1,17 @@ +import { Component } from "preact"; +import { TaskDetail, TaskStatus } from "../task.ts"; + +type Props = { + task: TaskDetail; +}; + +export default class Task extends Component { + render() { + const task = this.props.task; + return ( +
+ Task Id: {task.base.id} +
+ ); + } +} diff --git a/islands/TaskManager.tsx b/islands/TaskManager.tsx index 10c66a1..7a44610 100644 --- a/islands/TaskManager.tsx +++ b/islands/TaskManager.tsx @@ -1,28 +1,48 @@ import { Component, ContextType } from "preact"; -import { useEffect, useRef, useState } from "preact/hooks"; +import { useEffect, useRef } from "preact/hooks"; +import { signal } from "@preact/signals"; import { GlobalCtx } from "../components/GlobalContext.tsx"; import { TaskDetail, TaskStatus } from "../task.ts"; import { Sortable } from "sortable"; import { TaskClientSocketData, TaskServerSocketData } from "../server/task.ts"; import { get_ws_host } from "../server/utils.ts"; +import Task from "../components/Task.tsx"; export type TaskManagerProps = { show: boolean; }; +const tasks = signal(new Map()); +const task_list = signal(new Array()); + +type SortableEvent = CustomEvent & { + oldIndex: number | undefined; + newIndex: number | undefined; +}; + export default class TaskManager extends Component { static contextType = GlobalCtx; declare context: ContextType; + sortable?: Sortable; render() { - if (!this.props.show) return null; - const [tasks, set_tasks] = useState>(new Map()); const ul = useRef(); useEffect(() => { - new Sortable(ul.current, { - onSort: (evt: CustomEvent) => { - console.log(evt); + if (!this.props.show) { + this.sortable?.destroy(); + this.sortable = undefined; + return; + } + this.sortable = new Sortable(ul.current, { + onSort: (evt: SortableEvent) => { + if ( + evt.newIndex === undefined || evt.oldIndex === undefined + ) return; + const tl = task_list.value; + tl.splice(evt.newIndex, 0, tl.splice(evt.oldIndex, 1)[0]); }, }); + }, [this.props.show]); + useEffect(() => { const ws = new WebSocket(`${get_ws_host()}/api/task`); console.log(ws); function sendMessage(mes: TaskClientSocketData) { @@ -36,34 +56,47 @@ export default class TaskManager extends Component { if (t.type == "close") { ws.close(); } else if (t.type == "tasks") { - set_tasks((tasks) => { - t.tasks.forEach((ta) => { - tasks.set(ta.id, { - base: ta, - status: t.running.includes(ta.id) - ? TaskStatus.Running - : TaskStatus.Wait, - }); + let running_index = -1; + t.tasks.forEach((ta) => { + const is_running = t.running.includes(ta.id); + tasks.value.set(ta.id, { + base: ta, + status: is_running + ? TaskStatus.Running + : TaskStatus.Wait, }); - this.forceUpdate(); - return tasks; + const tl = task_list.value; + if (!tl.includes(ta.id)) { + tl.push(ta.id); + if (is_running) { + if (tl.length) { + tl.splice( + running_index + 1, + 0, + tl.splice(tl.length - 1, 1)[0], + ); + } + running_index += 1; + } + } }); + this.forceUpdate(); } else if (t.type == "new_task") { - set_tasks((tasks) => { - tasks.set(t.detail.id, { - base: t.detail, - status: TaskStatus.Wait, - }); - this.forceUpdate(); - return tasks; + tasks.value.set(t.detail.id, { + base: t.detail, + status: TaskStatus.Wait, }); + if (!task_list.value.includes(t.detail.id)) { + task_list.value.push(t.detail.id); + } + this.forceUpdate(); } }; self.addEventListener("beforeunload", () => { sendMessage({ type: "close" }); }); }, []); - console.log(tasks.size); + if (!this.props.show) return null; return (
{ // @ts-ignore checked ref={ul} > - {Array.from(tasks.keys()).map((k) =>
{k}
)} + {task_list.value.map((k) => { + const t = tasks.value.get(k); + if (t) { + return ; + } else { + return
; + } + })}
);