fix: harden popup empty state UX and make it self-healing#40
Merged
BitHighlander merged 3 commits intodevelopfrom Apr 21, 2026
Merged
fix: harden popup empty state UX and make it self-healing#40BitHighlander merged 3 commits intodevelopfrom
BitHighlander merged 3 commits intodevelopfrom
Conversation
Addresses the "popup open but says 'no events' with no recovery" failure mode, plus a latent crash if requestStorage returns null. Events.tsx - Null-guard requestStorage.getEvents() — previously a null return would throw on `for...of null` and white-screen the popup. - Wrap the fetch in try/catch and render a dedicated error state instead of crashing. - Subscribe to requestStorage changes so a second request arriving while the popup is open becomes visible immediately (no need to resolve the current request first). - Auto-close the window 3s after landing in an empty state. Covers the case where a request was cancelled upstream, where cleanup ran before the window closed itself, or where the popup was opened with no pending request. - Better copy in the empty / loading / error states so the user knows what is happening and that the window will close itself. - Keep currentIndex in bounds if the event list shrinks beneath it. Popup.tsx - Replace the placeholder "Error Occur" / "Loading ..." fallbacks with a proper Chakra-styled error panel that includes a Close button, so a rendering crash doesn't leave the user with no way out other than clicking the OS window close button. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The useEffect-based bounds check snapped currentIndex back only AFTER the render that caused it to go out of bounds. When requestStorage's subscribe fired and the event list shrank beneath currentIndex, the first render after the shrink still had the stale index — passing events[currentIndex] = undefined into <Transaction />, which reads event.id immediately and error-boundary crashes. Compute a clamped safeIndex during render instead. currentIndex as state is preserved for user-driven navigation; only the array access is guarded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The error-state UI copy already said the window would close itself, but the auto-close effect bailed on fetchError, leaving the user stuck on a dead-end error screen — the exact failure mode this PR is trying to remove. Collapse the two conditions into a single shouldAutoClose predicate so any non-actionable state (empty OR fetch failure) triggers the timer. Cleanup still runs correctly on recovery (error clears → new events arrive → previous cleanup cancels the timer, new effect short-circuits). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Companion to #39 (popup lifecycle hardening). That PR fixes why the popup can end up stuck; this PR fixes what the user sees when it does.
Targets three failure modes:
Changes
`pages/popup/src/components/Events.tsx` (rewritten)
`pages/popup/src/Popup.tsx`
Test plan
🤖 Generated with Claude Code