forked from Goutam/lynkeduppro-crm
perf(dashboard): stop refetching on every tab switch; own the QueryClient
Two changes, together removing nearly all the redundant conversation/history calls visible in the network panel. Keep-alive modules: Messenger and Inbox were rendered from the tab ternary, so every tab change UNMOUNTED them — all their state died and returning re-hit the API. They now mount on first visit and stay mounted, hidden with CSS. Deliberately mount-on-first-visit rather than eager-mount-both, so opening the dashboard does not fire two modules' initial loads for a user who never opens either. QueryClient ownership: the SDK (0.2.0) takes React Query as a peer and falls back to its own client if it can't find one — but then its cache would be invisible to the CRM. Providing it here keeps SDK and CRM data in one store, so a write can invalidate a conversation list. Client is created in useState, not at module scope, so an SSR render can't leak one user's cache into another's. refetchOnWindowFocus is off: the socket already pushes live changes, so focus refetching was pure noise. Also declares onnxruntime-web@1.21.0, a PINNED peerDependency of @imgly/background-removal that npm prunes on any re-resolve. The committed lock had it, so `npm ci` worked and `npm install` silently broke the gallery build — declaring it directly makes it unprunable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import { Settings } from "./settings";
|
||||
import { Projects } from "./projects";
|
||||
import { NotificationCenter } from "./notification-center";
|
||||
import { RealtimeProvider } from "@/lib/realtime";
|
||||
import { CrmQueryProvider } from "@/lib/query-client";
|
||||
import { SmartGallery } from "./smart-gallery";
|
||||
import { Leads } from "./leads";
|
||||
import { Verify } from "./verify";
|
||||
@@ -32,6 +33,16 @@ export function Dashboard() {
|
||||
const [active, setActive] = useState("dashboard");
|
||||
// Deep link from global search: which conversation to focus once we switch tabs.
|
||||
const [deepLink, setDeepLink] = useState<{ surface: "messenger" | "inbox"; threadId: string } | null>(null);
|
||||
// Messenger and Inbox are KEPT MOUNTED once opened, and hidden with CSS when you switch tabs.
|
||||
// Rendering them from the ternary below unmounted them on every tab change, which threw away
|
||||
// their sockets' worth of state and re-hit the API on return. Mount-on-first-visit rather than
|
||||
// eager-mount-both, so the dashboard doesn't fire two modules' initial loads at boot.
|
||||
const [everOpened, setEverOpened] = useState<Set<string>>(new Set());
|
||||
useEffect(() => {
|
||||
if (active === "messenger" || active === "inbox") {
|
||||
setEverOpened((prev) => (prev.has(active) ? prev : new Set(prev).add(active)));
|
||||
}
|
||||
}, [active]);
|
||||
|
||||
function navigateToConversation(surface: "messenger" | "inbox", threadId: string) {
|
||||
setActive(surface);
|
||||
@@ -72,6 +83,7 @@ export function Dashboard() {
|
||||
|
||||
return (
|
||||
<div className="dash-root" data-theme={theme}>
|
||||
<CrmQueryProvider>
|
||||
<RealtimeProvider>
|
||||
<Sidebar active={active} onSelect={setActive} />
|
||||
<div className="dash-main">
|
||||
@@ -79,12 +91,21 @@ export function Dashboard() {
|
||||
<div className="dash-content">
|
||||
<ToastProvider>
|
||||
<NotificationCenter active={active} onNavigate={navigateToConversation} />
|
||||
{active === "profile" ? <Profile />
|
||||
{everOpened.has("messenger") ? (
|
||||
<div hidden={active !== "messenger"} className="dash-keepalive">
|
||||
<MessengerSdk focusThreadId={deepLink?.surface === "messenger" ? deepLink.threadId : null} />
|
||||
</div>
|
||||
) : null}
|
||||
{everOpened.has("inbox") ? (
|
||||
<div hidden={active !== "inbox"} className="dash-keepalive">
|
||||
<InboxSdk focusThreadId={deepLink?.surface === "inbox" ? deepLink.threadId : null} />
|
||||
</div>
|
||||
) : null}
|
||||
{active === "messenger" || active === "inbox" ? null
|
||||
: active === "profile" ? <Profile />
|
||||
: active === "support" ? <Support />
|
||||
: active === "rules" ? <Rules />
|
||||
: active === "ai" ? <AiAssistant />
|
||||
: active === "messenger" ? <MessengerSdk focusThreadId={deepLink?.surface === "messenger" ? deepLink.threadId : null} />
|
||||
: active === "inbox" ? <InboxSdk focusThreadId={deepLink?.surface === "inbox" ? deepLink.threadId : null} />
|
||||
: active === "settings" ? <Settings />
|
||||
: active === "gallery" ? <SmartGallery theme={theme} />
|
||||
: active === "leads" ? <Leads />
|
||||
@@ -97,6 +118,7 @@ export function Dashboard() {
|
||||
</div>
|
||||
</div>
|
||||
</RealtimeProvider>
|
||||
</CrmQueryProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user