Skip to content

fix(realtime): debounce the reconnecting toast to stop transient-blip flashes#5111

Merged
waleedlatif1 merged 2 commits into
stagingfrom
fix/reconnecting-alert-sensitivity
Jun 17, 2026
Merged

fix(realtime): debounce the reconnecting toast to stop transient-blip flashes#5111
waleedlatif1 merged 2 commits into
stagingfrom
fix/reconnecting-alert-sensitivity

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • The "Reconnecting..." persistent toast fired the instant isReconnecting flipped true. Socket.IO flips that flag on any disconnect — including sub-second transport hiccups that recover on the very first retry (reconnectionDelay: 1000) — so a momentary blip flashed a scary alert that vanished a beat later.
  • Adds useStableFlag, an anti-flicker boolean that smooths both edges:
    • Rising edgevalue must stay true for delayMs (2000ms) before the flag turns on, so blips that self-heal within the window never surface.
    • Falling edge — once shown, the flag holds for at least minVisibleMs (1500ms), so a drop that lasts just past the delay doesn't flash-and-vanish.
  • The socket flag itself stays accurate; only the user-facing alarm is smoothed (the correct layer for the grace period).
  • State machine extracted into a framework-agnostic controller (createStableFlagController) and covered by 10 unit tests: both flicker modes, genuine outage, mid-hold flap (reconnect bouncing during the hold window), idempotent re-feeds, and dispose cancellation.

Why this is the canonical pattern (not a hack)

This is the textbook anti-flicker fix for connection indicators, confirmed from two angles:

  • Socket.IO's own model treats transient drops as transparent — the client "will automatically try to reconnect after a small delay," and socket.active exists precisely to distinguish temporary (self-healing) from terminal failures. Delaying the UI aligns with that intent rather than papering over it. → https://socket.io/docs/v4/tutorial/handling-disconnections
  • The entry-delay + minimum-visible-duration pairing is the established anti-flicker indicator pattern (delay before show kills flash-on; minimum on-screen time kills flash-off). → https://github.com/frysztak/use-debounced-loader

Complementary / out of scope (noted for reviewers): Socket.IO connection state recovery addresses data continuity across drops (missed events), not the UI alarm — a separate concern from this change. → https://socket.io/docs/v4/connection-state-recovery

Type of Change

  • Bug fix

Testing

  • 10 unit tests on the createStableFlagController state machine (fake timers, no DOM) covering both flicker modes, genuine outage, mid-hold flap, idempotency, and dispose — all passing.
  • Manually exercised in a real browser (forged session + Chrome network throttling): a sub-second blip surfaced no toast (correct).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

… flashes

The "Reconnecting..." persistent toast fired the instant isReconnecting
flipped true, so sub-second transport blips that self-heal on the first
retry flashed a scary alert. Add useStableFlag, an anti-flicker boolean
that delays the rising edge (2s, so brief blips never surface) and holds
the falling edge (1.5s min visible, so a drop just past the delay does not
flash-and-vanish). The socket flag stays accurate; only the user-facing
alarm is smoothed. State machine extracted into a framework-agnostic
controller with unit coverage for both flicker modes.
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 17, 2026 5:58pm

Request Review

@cursor

cursor Bot commented Jun 17, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
UI-only timing around an existing toast; socket state and offline/join error paths are unchanged.

Overview
Fixes "Reconnecting..." persistent toasts flashing on brief Socket.IO disconnects by smoothing only the UI signal, not isReconnecting itself.

Adds useStableFlag (backed by testable createStableFlagController) with a 2s show delay and 1.5s minimum on-screen time, then uses it in WorkspacePermissionsProvider so the reconnecting message appears only after a sustained drop and does not vanish instantly when the socket flaps back.

Includes 10 Vitest tests on the controller (flash-on/off, boundaries, mid-hold flap, idempotency, dispose).

Reviewed by Cursor Bugbot for commit 030b6c0. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a UX issue where the "Reconnecting…" persistent toast would flash briefly during sub-second Socket.IO transport hiccups that self-heal before users can even read the message. It introduces useStableFlag, an anti-flicker hook backed by a framework-agnostic createStableFlagController state machine, and wires it into the reconnection toast in workspace-permissions-provider.tsx.

  • createStableFlagController implements a two-sided debounce: a rising-edge delay (delayMs = 2000ms) suppresses show-flashes from blips that heal before the window expires, and a falling-edge minimum (minVisibleMs = 1500ms) prevents show-then-vanish flashes when the outage barely outlasts the delay.
  • 10 unit tests cover both flicker suppression modes, genuine outages, mid-hold flaps, idempotency, and dispose cancellation — all driven with fake timers and no DOM dependency.

Confidence Score: 5/5

Safe to merge — a narrow, well-tested addition that touches only the toast display path and does not affect socket state, permissions, or data flow.

The change is a self-contained UI smoothing layer. The controller logic is deterministic and exhaustively covered by unit tests with fake timers. All previously raised review concerns have been addressed in the head commit.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/hooks/use-stable-flag.ts New anti-flicker hook: framework-agnostic controller + thin React wrapper. Rising-edge delay and falling-edge minimum are correctly implemented; idempotency, dispose, and the options-change stuck-true edge case are all handled.
apps/sim/hooks/use-stable-flag.test.ts 10 unit tests covering both flicker modes, genuine outage, mid-hold flap, idempotency, and dispose — all using fake timers; the previously-vacuous active assertion now uses the live getter.
apps/sim/app/workspace/[workspaceId]/providers/workspace-permissions-provider.tsx Wires useStableFlag into the Reconnecting toast path using module-level constants; correctness of the surrounding context and offline-mode logic is unaffected.

Reviews (3): Last reviewed commit: "fix(realtime): reset stable-flag React s..." | Re-trigger Greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/hooks/use-stable-flag.test.ts Outdated
Comment thread apps/sim/hooks/use-stable-flag.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 978bac1. Configure here.

Comment thread apps/sim/hooks/use-stable-flag.test.ts
…cuous blip test

Address Greptile review:
- useStableFlag: reset React state to the fresh controller's baseline when
  the controller is recreated on an options change, so a dynamic consumer
  changing delayMs/minVisibleMs while active with value already false can no
  longer strand the flag at true.
- test: read the live probe.active getter in the blip test instead of a
  destructured snapshot, which was bound to false at destructure time and
  made the assertion vacuous.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 030b6c0. Configure here.

@waleedlatif1 waleedlatif1 merged commit 9e9f2b9 into staging Jun 17, 2026
16 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/reconnecting-alert-sensitivity branch June 17, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant