From fd7a1a0264394109202c5dec8438ff153e449166 Mon Sep 17 00:00:00 2001 From: Simon Pan <263902872+chsimonpan@users.noreply.github.com> Date: Wed, 8 Jul 2026 10:20:08 +0800 Subject: [PATCH] fix(sidebar): refresh and load channel-created sessions Pre-commit hook ran. Total eslint: 6, total circular: 0 --- .../sidebarSessionRefresh.ts | 51 +++++++++++++++++++ .../connectors/sidebarConnectorUtils.ts | 10 +++- src/store/session/sessionAtom/loaders.ts | 8 ++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/scaffold/NavigationSidebar/connectors/WorkstationSidebarConnector/sidebarSessionRefresh.ts b/src/scaffold/NavigationSidebar/connectors/WorkstationSidebarConnector/sidebarSessionRefresh.ts index 51c4459d8..acaf7e6fb 100644 --- a/src/scaffold/NavigationSidebar/connectors/WorkstationSidebarConnector/sidebarSessionRefresh.ts +++ b/src/scaffold/NavigationSidebar/connectors/WorkstationSidebarConnector/sidebarSessionRefresh.ts @@ -42,8 +42,59 @@ export async function rescanSidebarSessions(): Promise { }); } +import { + SIDEBAR_SESSION_ACTIVE_REFRESH_INTERVAL_MS, + SIDEBAR_SESSION_IDLE_REFRESH_INTERVAL_MS, +} from "../sidebarConnectorUtils"; + export function useSidebarSessionRefreshEffects(): void { useEffect(() => { void loadSidebarSessions({ forceRefresh: true }); }, []); + + + useEffect(() => { + let sidebarIntervalId: number | null = null; + + const getSidebarRefreshInterval = () => + document.hasFocus() + ? SIDEBAR_SESSION_ACTIVE_REFRESH_INTERVAL_MS + : SIDEBAR_SESSION_IDLE_REFRESH_INTERVAL_MS; + + const refreshAllSidebarSessions = () => { + if (document.visibilityState !== "visible") return; + void loadSidebarSessions({ forceRefresh: true }); + }; + + const scheduleRefresh = () => { + if (sidebarIntervalId !== null) { + window.clearInterval(sidebarIntervalId); + sidebarIntervalId = null; + } + if (document.visibilityState !== "visible") return; + sidebarIntervalId = window.setInterval( + refreshAllSidebarSessions, + getSidebarRefreshInterval() + ); + }; + + const handleActivityStateChange = () => { + refreshAllSidebarSessions(); + scheduleRefresh(); + }; + + scheduleRefresh(); + document.addEventListener("visibilitychange", handleActivityStateChange); + window.addEventListener("focus", handleActivityStateChange); + window.addEventListener("blur", scheduleRefresh); + return () => { + if (sidebarIntervalId !== null) window.clearInterval(sidebarIntervalId); + document.removeEventListener( + "visibilitychange", + handleActivityStateChange + ); + window.removeEventListener("focus", handleActivityStateChange); + window.removeEventListener("blur", scheduleRefresh); + }; + }, []); } diff --git a/src/scaffold/NavigationSidebar/connectors/sidebarConnectorUtils.ts b/src/scaffold/NavigationSidebar/connectors/sidebarConnectorUtils.ts index a22e569cc..d687cc77c 100644 --- a/src/scaffold/NavigationSidebar/connectors/sidebarConnectorUtils.ts +++ b/src/scaffold/NavigationSidebar/connectors/sidebarConnectorUtils.ts @@ -8,7 +8,15 @@ import type { SessionCreatorDraft } from "@src/store/session"; // ── Constants ───────────────────────────────────────────────────────────────── -/** Poll interval for Cursor IDE session list refresh. */ +/** Poll intervals for sidebar session list refresh. */ +export const CURSOR_IDE_ACTIVE_REFRESH_INTERVAL_MS = 60_000; +export const CURSOR_IDE_IDLE_REFRESH_INTERVAL_MS = 5 * 60_000; + +// ORG2 channel sessions can be created from external surfaces (Feishu, etc.) +// without any frontend action. Refresh the full sidebar list while focused so +// `/newsession ...` appears in ORG2 without a manual page reload. +export const SIDEBAR_SESSION_ACTIVE_REFRESH_INTERVAL_MS = 15_000; +export const SIDEBAR_SESSION_IDLE_REFRESH_INTERVAL_MS = 60_000; export const NEW_SESSION_MENU_ITEM_ID = "new-session"; export const PROJECTS_NEW_PROJECT_MENU_ITEM_ID = "projects-new-project"; diff --git a/src/store/session/sessionAtom/loaders.ts b/src/store/session/sessionAtom/loaders.ts index 9400c852d..8cdc6bb3c 100644 --- a/src/store/session/sessionAtom/loaders.ts +++ b/src/store/session/sessionAtom/loaders.ts @@ -382,7 +382,7 @@ interface FetchPageResult { } async function fetchAggregatePage( - wireCategory: "cli" | "agent", + wireCategory: "cli" | "agent" | "agent,os", offset: number, pageSize: number ): Promise { @@ -419,7 +419,11 @@ async function loadCategoryPage( case "cli_agent": return fetchAggregatePage("cli", offset, pageSize); case "rust_agent": - return fetchAggregatePage("agent", offset, pageSize); + // Rust-native sessions include both SDE/custom agent rows (category=agent) + // and channel/desktop OS rows (category=os, e.g. osagent-feishu-*). + // The sidebar groups them under one Rust Agent bucket, so fetch both; + // otherwise Feishu channel sessions exist in DB but never appear/open. + return fetchAggregatePage("agent,os", offset, pageSize); } }