Files
nanobot/webui/src/hooks/useSessionAutomationJobs.ts
T
ab9f49970d feat(desktop): polish desktop shell and shared WebUI surfaces (#4195)
* feat(desktop): add native host scaffold

* feat(webui): track turns and usage in gateway

* feat(webui): polish desktop chat experience

* feat(apps): add ArcGIS and Joplin logos

* feat(desktop): polish shell and shared surfaces

* fix(webui): avoid preview chips for glob references

* test: align CI expectations for token fallback

* feat(webui): preview prompt rail entries

* feat(webui): add prompt navigator drawer

* style(webui): refine prompt navigator placement

* style(webui): align prompt navigator with header actions

* style(webui): simplify prompt navigator header

* refactor(webui): clean thread resource refresh

* feat(desktop): add native reply notifications

* fix(webui): preserve desktop restart and replay state

* fix(desktop): harden gateway proxy startup

* fix(web): fall back when readability is unavailable

* fix(desktop): hide window instead of closing on macos

* fix(webui): unify desktop header actions

* fix(webui): simplify prompt history rows

* fix(desktop): log notification delivery failures

* chore(desktop): clean source package artifacts

* fix(cron): support one-time relative reminders

* fix(webui): reveal scroll button in place

* Revert "fix(cron): support one-time relative reminders"

This reverts commit 4c4661da120a3c7283e0768412bae48604e7390b.

* refactor(webui): extract token usage heatmap

* docs(desktop): clarify contributor guides

---------

Co-authored-by: chengyongru <2755839590@qq.com>
2026-06-06 19:49:33 +08:00

62 lines
1.9 KiB
TypeScript

import { useEffect, useState } from "react";
import { fetchSessionAutomations } from "@/lib/api";
import type { SessionAutomationJob } from "@/lib/types";
const AUTOMATIONS_REFRESH_MS = 3000;
export function useSessionAutomationJobs(open: boolean, token: string, sessionKey: string) {
const [jobs, setJobs] = useState<SessionAutomationJob[]>([]);
const [loading, setLoading] = useState(false);
const [loadFailed, setLoadFailed] = useState(false);
const [now, setNow] = useState(() => Date.now());
useEffect(() => {
if (!open) return;
let cancelled = false;
let loadedOnce = false;
const refresh = async (showLoading = false) => {
if (showLoading) {
setLoading(true);
setLoadFailed(false);
setJobs([]);
}
try {
const next = await fetchSessionAutomations(token, sessionKey);
if (cancelled) return;
setJobs(next.jobs);
setLoadFailed(false);
loadedOnce = true;
} catch {
if (!cancelled && !loadedOnce) setLoadFailed(true);
} finally {
if (!cancelled && showLoading) setLoading(false);
}
};
void refresh(true);
const refreshId = window.setInterval(() => void refresh(false), AUTOMATIONS_REFRESH_MS);
const refreshOnFocus = () => {
if (document.visibilityState !== "hidden") void refresh(false);
};
window.addEventListener("focus", refreshOnFocus);
document.addEventListener("visibilitychange", refreshOnFocus);
return () => {
cancelled = true;
window.clearInterval(refreshId);
window.removeEventListener("focus", refreshOnFocus);
document.removeEventListener("visibilitychange", refreshOnFocus);
};
}, [open, sessionKey, token]);
useEffect(() => {
if (!open) return;
setNow(Date.now());
const tickId = window.setInterval(() => setNow(Date.now()), 1000);
return () => window.clearInterval(tickId);
}, [open]);
return { jobs, loading, loadFailed, now };
}