Skip to content

🐛 Fix mode switching requiring manual browser refresh#760

Merged
clubanderson merged 1 commit intomainfrom
fix/mode-switch-reactive-hooks
Feb 9, 2026
Merged

🐛 Fix mode switching requiring manual browser refresh#760
clubanderson merged 1 commit intomainfrom
fix/mode-switch-reactive-hooks

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Summary

Fix mode switching (demo ↔ live) requiring manual browser refresh to see updated content.

Problem

When switching between demo mode and live mode, cards showed "Demo" badges but content remained skeletons. Data only appeared after manually refreshing the browser.

Root Cause

The useCachedData wrapper hooks used isDemoMode() function which evaluates once at mount time:

// OLD - evaluates once, never updates
enabled: !isDemoMode()

When demo mode changed, this value never updated because the parent component had no dependency on demo mode.

Fix

Replace static isDemoMode() function with reactive useDemoMode() hook:

// NEW - subscribes to changes, triggers re-render
const { isDemoMode: demoMode } = useDemoMode()
// ...
enabled: !demoMode

The hook subscribes to demo mode changes and triggers component re-renders, which causes useCache to receive the updated enabled value.

Hooks Updated

  • useCachedPods
  • useCachedEvents
  • useCachedPodIssues
  • useCachedDeploymentIssues
  • useCachedDeployments
  • useCachedServices
  • useCachedProwJobs
  • useCachedLLMdServers
  • useCachedLLMdModels
  • useCachedSecurityIssues

Test Plan

  • Start in live mode with data showing
  • Toggle to demo mode - verify demo data appears immediately (no manual refresh)
  • Toggle back to live mode - verify live data appears immediately
  • Build passes

🤖 Generated with Claude Code

Replace static `isDemoMode()` function calls with reactive `useDemoMode()`
hook in all `useCachedData` wrapper hooks.

The problem was that `enabled: !isDemoMode()` evaluated once at mount time.
When demo mode changed, this value never updated because:
1. The parent component didn't re-render (no dependency on demo mode)
2. `enabled` was computed at mount time and stayed fixed

The fix: Use `useDemoMode()` hook which subscribes to demo mode changes
and triggers re-renders. This causes `useCache` to receive the updated
`enabled` value when mode switches.

Hooks updated:
- useCachedPods
- useCachedEvents
- useCachedPodIssues
- useCachedDeploymentIssues
- useCachedDeployments
- useCachedServices
- useCachedProwJobs
- useCachedLLMdServers
- useCachedLLMdModels
- useCachedSecurityIssues

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Signed-off-by: Andrew Anderson <[email protected]>
Copilot AI review requested due to automatic review settings February 9, 2026 17:57
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Feb 9, 2026
@kubestellar-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@clubanderson clubanderson merged commit 78eee62 into main Feb 9, 2026
10 checks passed
@netlify
Copy link
Copy Markdown

netlify Bot commented Feb 9, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit faa3732
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/698a1ff2432c7800089d0c20
😎 Deploy Preview https://deploy-preview-760.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 9, 2026

Welcome to KubeStellar! 🚀 Thank you for submitting this Pull Request.

Before your PR can be merged, please ensure:

DCO Sign-off - All commits must be signed off with git commit -s to certify the Developer Certificate of Origin

PR Title - Must start with an emoji: ✨ (feature), 🐛 (bug fix), 📖 (docs), 🌱 (infra/tests), ⚠️ (breaking change)

Getting Started with KubeStellar:

Contributor Resources:


🌟 Help KubeStellar Grow - We Need Adopters!

Our roadmap is driven entirely by adopter feedback. Whether you're using KubeStellar yourself or know someone who could benefit from multi-cluster Kubernetes:

📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction!


A maintainer will review your PR soon. Feel free to ask questions in the comments or on Slack!

@kubestellar-prow kubestellar-prow Bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Feb 9, 2026
@kubestellar-prow kubestellar-prow Bot deleted the fix/mode-switch-reactive-hooks branch February 9, 2026 17:57
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 9, 2026

🎉 Thank you for your contribution! Your PR has been successfully merged.


🌟 Help KubeStellar Grow - We Need Adopters!

Our roadmap is driven entirely by adopter feedback - nothing else. Whether you're using KubeStellar yourself or know organizations that could benefit from multi-cluster Kubernetes, we need your help:

📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction!

🗣️ Spread the word - Tell colleagues, write blog posts, present at meetups

💬 Share feedback on Slack #kubestellar-dev

Every adopter story helps us prioritize what matters most. Thank you for being part of the KubeStellar community!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes stale cached-hook behavior when toggling demo ↔ live mode by making the cache “enabled” flag reactive to demo mode changes, so hooks refetch (or stop fetching) immediately without requiring a browser refresh.

Changes:

  • Import and use useDemoMode() in cached data hooks to derive a reactive enabled value.
  • Update multiple useCached* hooks to use enabled: !demoMode instead of enabled: !isDemoMode().
  • Ensures mode toggles propagate into the caching layer, allowing immediate data refresh behavior.

Comment on lines +349 to +357
const { isDemoMode: demoMode } = useDemoMode()
const { limit = 20, category = 'realtime' } = options || {}
const key = `events:${cluster || 'all'}:${namespace || 'all'}:${limit}`

const result = useCache({
key,
category,
initialData: getDemoEvents(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: consider removing the per-hook useDemoMode() + enabled: !demoMode and rely on useCache’s internal demo-mode gating to reduce repeated subscriptions/boilerplate.

Copilot uses AI. Check for mistakes.
Comment on lines +386 to +394
const { isDemoMode: demoMode } = useDemoMode()
const { category = 'pods' } = options || {}
const key = `podIssues:${cluster || 'all'}:${namespace || 'all'}`

const result = useCache({
key,
category,
initialData: getDemoPodIssues(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: useCache already handles demo mode; using useDemoMode() here is redundant and adds repeated subscriptions/boilerplate across hooks.

Copilot uses AI. Check for mistakes.
Comment on lines +450 to +458
const { isDemoMode: demoMode } = useDemoMode()
const { category = 'deployments' } = options || {}
const key = `deploymentIssues:${cluster || 'all'}:${namespace || 'all'}`

const result = useCache({
key,
category,
initialData: getDemoDeploymentIssues(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: the useDemoMode() + enabled: !demoMode pattern is redundant with useCache’s internal demo-mode gating and could be removed to keep these hooks smaller.

Copilot uses AI. Check for mistakes.
Comment on lines +526 to +534
const { isDemoMode: demoMode } = useDemoMode()
const { category = 'deployments' } = options || {}
const key = `deployments:${cluster || 'all'}:${namespace || 'all'}`

const result = useCache({
key,
category,
initialData: getDemoDeployments(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: consider relying on useCache’s internal demo-mode gating instead of adding useDemoMode() + enabled: !demoMode in each cached hook.

Copilot uses AI. Check for mistakes.
category,
initialData: getDemoServices(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: useCache already disables fetching in demo mode; consider omitting the enabled: !demoMode override here to avoid duplicated demo-mode subscriptions.

Suggested change
enabled: !demoMode,

Copilot uses AI. Check for mistakes.
Comment on lines +1071 to +1078
const { isDemoMode: demoMode } = useDemoMode()
const key = `llmd-servers:${clusters.join(',')}`

const result = useCache({
key,
category: 'gitops',
initialData: getDemoLLMdServers(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: consider removing the per-hook demo-mode subscription and letting useCache’s internal demo-mode gating control fetching/returned data.

Copilot uses AI. Check for mistakes.
Comment on lines +1262 to +1270
const { isDemoMode: demoMode } = useDemoMode()
const { category = 'pods' } = options || {}
const key = `securityIssues:${cluster || 'all'}:${namespace || 'all'}`

const result = useCache({
key,
category,
initialData: getDemoSecurityIssues(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: useCache already disables fetching/returns initialData in demo mode; consider removing the extra useDemoMode() + enabled override here.

Copilot uses AI. Check for mistakes.
Comment on lines +302 to +310
const { isDemoMode: demoMode } = useDemoMode()
const { limit = 100, category = 'pods' } = options || {}
const key = `pods:${cluster || 'all'}:${namespace || 'all'}:${limit}`

const result = useCache({
key,
category,
initialData: getDemoPods(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCache already subscribes to demo mode internally and gates fetching / returned data based on demo mode (see web/src/lib/cache/index.ts where effectiveEnabled = enabled && !demoMode). Because of that, you can omit the enabled override here (or leave enabled for non-demo conditions) and avoid adding useDemoMode() + duplicated state subscriptions in every cached hook.

Copilot uses AI. Check for mistakes.
Comment on lines +761 to +768
const { isDemoMode: demoMode } = useDemoMode()
const key = `prowjobs:${prowCluster}:${namespace}`

const result = useCache({
key,
category: 'gitops',
initialData: getDemoProwJobs(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: since useCache already gates on demo mode internally, the added useDemoMode() + enabled: !demoMode here is redundant and could be simplified.

Copilot uses AI. Check for mistakes.
Comment on lines +1136 to +1143
const { isDemoMode: demoMode } = useDemoMode()
const key = `llmd-models:${clusters.join(',')}`

const result = useCache({
key,
category: 'gitops',
initialData: getDemoLLMdModels(),
enabled: !isDemoMode(),
enabled: !demoMode,
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: useCache already gates demo mode internally; the useDemoMode() + enabled override here is redundant boilerplate.

Copilot uses AI. Check for mistakes.
clubanderson added a commit that referenced this pull request Feb 13, 2026
Replace static `isDemoMode()` function calls with reactive `useDemoMode()`
hook in all `useCachedData` wrapper hooks.

The problem was that `enabled: !isDemoMode()` evaluated once at mount time.
When demo mode changed, this value never updated because:
1. The parent component didn't re-render (no dependency on demo mode)
2. `enabled` was computed at mount time and stayed fixed

The fix: Use `useDemoMode()` hook which subscribes to demo mode changes
and triggers re-renders. This causes `useCache` to receive the updated
`enabled` value when mode switches.

Hooks updated:
- useCachedPods
- useCachedEvents
- useCachedPodIssues
- useCachedDeploymentIssues
- useCachedDeployments
- useCachedServices
- useCachedProwJobs
- useCachedLLMdServers
- useCachedLLMdModels
- useCachedSecurityIssues

Signed-off-by: Andrew Anderson <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
clubanderson added a commit that referenced this pull request Apr 15, 2026
Fixes #8175

The `builds wss: URL when page uses https` test in useExecSession-expand
was asserting the pre-phase3d URL shape — `wss://<page-host>/ws/exec` —
built from `window.location.protocol`. After #8168 migrated pod exec to
kc-agent (#7993), the hook uses the `LOCAL_AGENT_WS_URL` constant
(`ws://127.0.0.1:8585/ws`) unconditionally, because the SPDY exec stream
now runs under the user's own kubeconfig on the local loopback rather
than through the page origin. The page protocol no longer influences the
URL, so the old assertion has been stale since #8168 merged.

This is a pre-existing flake that Coverage Suite run #760 happened to
surface on top of b828c87 (#8169). #8169 only touched
useDashboardHealth, MSW handlers, and useDashboardHealth.test.ts — none
of which are related to exec session URL construction. Confirmed by
running the test in isolation and together with useDashboardHealth.test.ts
(both pass after the fix; isolated failure reproduces on unmodified
main against useExecSession.ts:235).

Fix: rename the test to "builds kc-agent /ws/exec URL regardless of
page protocol" and assert the new, stable target. Documented the
rationale inline with source pointer to useExecSession.ts:235 so future
readers don't reintroduce the old assertion.

Signed-off-by: Andy Anderson <[email protected]>
clubanderson added a commit that referenced this pull request Apr 15, 2026
Fixes #8175

The `builds wss: URL when page uses https` test in useExecSession-expand
was asserting the pre-phase3d URL shape — `wss://<page-host>/ws/exec` —
built from `window.location.protocol`. After #8168 migrated pod exec to
kc-agent (#7993), the hook uses the `LOCAL_AGENT_WS_URL` constant
(`ws://127.0.0.1:8585/ws`) unconditionally, because the SPDY exec stream
now runs under the user's own kubeconfig on the local loopback rather
than through the page origin. The page protocol no longer influences the
URL, so the old assertion has been stale since #8168 merged.

This is a pre-existing flake that Coverage Suite run #760 happened to
surface on top of b828c87 (#8169). #8169 only touched
useDashboardHealth, MSW handlers, and useDashboardHealth.test.ts — none
of which are related to exec session URL construction. Confirmed by
running the test in isolation and together with useDashboardHealth.test.ts
(both pass after the fix; isolated failure reproduces on unmodified
main against useExecSession.ts:235).

Fix: rename the test to "builds kc-agent /ws/exec URL regardless of
page protocol" and assert the new, stable target. Documented the
rationale inline with source pointer to useExecSession.ts:235 so future
readers don't reintroduce the old assertion.

Signed-off-by: Andy Anderson <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/M Denotes a PR that changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants