mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-09 17:08:42 +08:00
feat: implement automatic call refresh on CallsPage
- Added a refreshCalls function to appStore to fetch updated call data. - Integrated useEffect in CallsPage to periodically refresh calls every 2 seconds and on visibility change.
This commit is contained in:
@@ -1,10 +1,34 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
import { CallTable } from "../components/CallTable";
|
import { CallTable } from "../components/CallTable";
|
||||||
import { PageContent } from "../components/layout/PageContent";
|
import { PageContent } from "../components/layout/PageContent";
|
||||||
import { appStore, useAppStore } from "../store/appStore";
|
import { appStore, useAppStore } from "../store/appStore";
|
||||||
import styles from "./CallsPage.module.scss";
|
import styles from "./CallsPage.module.scss";
|
||||||
|
|
||||||
|
const CALL_REFRESH_INTERVAL_MS = 2_000;
|
||||||
|
|
||||||
export function CallsPage() {
|
export function CallsPage() {
|
||||||
const { calls } = useAppStore();
|
const { calls } = useAppStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let disposed = false;
|
||||||
|
const refreshCalls = () => {
|
||||||
|
if (!disposed && document.visibilityState === "visible") {
|
||||||
|
void appStore.refreshCalls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
refreshCalls();
|
||||||
|
const interval = window.setInterval(refreshCalls, CALL_REFRESH_INTERVAL_MS);
|
||||||
|
window.addEventListener("focus", refreshCalls);
|
||||||
|
document.addEventListener("visibilitychange", refreshCalls);
|
||||||
|
return () => {
|
||||||
|
disposed = true;
|
||||||
|
window.clearInterval(interval);
|
||||||
|
window.removeEventListener("focus", refreshCalls);
|
||||||
|
document.removeEventListener("visibilitychange", refreshCalls);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const content = <div className={styles.page}><CallTable calls={calls} onDetails={(call) => void appStore.openCallDetails(call.call_id)} /></div>;
|
const content = <div className={styles.page}><CallTable calls={calls} onDetails={(call) => void appStore.openCallDetails(call.call_id)} /></div>;
|
||||||
return <PageContent fixed title={t("调用")} contentClassName={styles.pageContent} sections={[{ key: "calls", estimatedHeight: 720, content }]} />;
|
return <PageContent fixed title={t("调用")} contentClassName={styles.pageContent} sections={[{ key: "calls", estimatedHeight: 720, content }]} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,14 @@ export const appStore = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async refreshCalls() {
|
||||||
|
try {
|
||||||
|
update({ calls: await api.calls() });
|
||||||
|
} catch (cause) {
|
||||||
|
update({ error: cause instanceof Error ? cause.message : String(cause) });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async openCallDetails(callId: string) {
|
async openCallDetails(callId: string) {
|
||||||
await perform(() => api.openCallDetails(callId));
|
await perform(() => api.openCallDetails(callId));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -56,10 +56,9 @@ pub(super) fn project(
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
if reason != pb::BackgroundTaskCompletionReason::TaskFinished {
|
if reason != pb::BackgroundTaskCompletionReason::TaskFinished {
|
||||||
return Err(Error::Protocol(format!(
|
// Progress and reparenting notifications are informational; the
|
||||||
"background task notification is not a finished task: {}",
|
// client batches them together with the real finish notification.
|
||||||
reason.as_str_name()
|
continue;
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
if completion.task_id.is_empty() || completion.title.is_empty() {
|
if completion.task_id.is_empty() || completion.title.is_empty() {
|
||||||
return Err(Error::Protocol(
|
return Err(Error::Protocol(
|
||||||
@@ -110,7 +109,7 @@ pub(super) fn project(
|
|||||||
let (first, _) = completions
|
let (first, _) = completions
|
||||||
.values()
|
.values()
|
||||||
.next()
|
.next()
|
||||||
.expect("background completion action was validated as non-empty");
|
.ok_or_else(|| Error::Protocol("background task notification contains no finished task".into()))?;
|
||||||
let text = match (has_shell, has_subagent) {
|
let text = match (has_shell, has_subagent) {
|
||||||
(true, false) => SHELL_FOLLOW_UP.into(),
|
(true, false) => SHELL_FOLLOW_UP.into(),
|
||||||
(false, true) => FOLLOW_UP.into(),
|
(false, true) => FOLLOW_UP.into(),
|
||||||
@@ -362,7 +361,25 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap_err()
|
.unwrap_err()
|
||||||
.to_string()
|
.to_string()
|
||||||
.contains("not a finished task"));
|
.contains("no finished task"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn progress_notifications_batched_with_a_finish_are_ignored() {
|
||||||
|
let mut progress = completion();
|
||||||
|
progress.task_id = "child-id:task_progress:1".into();
|
||||||
|
progress.reason = pb::BackgroundTaskCompletionReason::TaskProgress as i32;
|
||||||
|
let projection = project(
|
||||||
|
&pb::BackgroundTaskCompletionAction {
|
||||||
|
completions: vec![progress, completion()],
|
||||||
|
},
|
||||||
|
pb::AgentMode::Agent as i32,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(projection.context.contains("agent_id: child-id"));
|
||||||
|
assert!(!projection.context.contains("task_progress"));
|
||||||
|
assert_eq!(projection.turn_user.text, FOLLOW_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion() -> pb::BackgroundTaskCompletion {
|
fn completion() -> pb::BackgroundTaskCompletion {
|
||||||
|
|||||||
Reference in New Issue
Block a user