Skip to content

fix(tui): show live spinner while waiting on QR init, clear stale banner - #67

Merged
oth-body merged 1 commit into
masterfrom
fix/qr-loading-state
Sep 14, 2026
Merged

oth-body merged 1 commit into
masterfrom
fix/qr-loading-state

Conversation

@oth-body

Copy link
Copy Markdown
Owner

Summary

The 'Scan with Amber' (NIP-46) screen said 'Generating QR code…' both while waiting AND after the QR had rendered — two real bugs that together looked like a hung program.

What was wrong

Bug 1 (the actual cause of "never loads"): handleLoginEnter sets m.message = "Generating QR code..." when the user picks the QR menu item. The qrGeneratedMsg handler stored m.qrData and m.qrReady correctly, but never cleared m.message. View() always appends m.message if non-empty. Result: the QR rendered correctly above a stale "Generating QR code…" banner. On a 24-line terminal where the QR is ~30 lines tall after lipgloss.Place centering, only the lower part of the screen is visible at any time — so most of the time the user sees the stale banner, never the QR, and concludes the program is stuck.

Bug 2 (the "no loading indication" complaint): the loading text was static. No animation, no spinner — nothing changed frame to frame — so even when the program genuinely was waiting on the network, there was no feedback that anything was happening.

Fix

tui/tui.go:

  • Added a spinner.Model field on Model, initialized in NewModel with the ASCII-only spinner.Line frame set (so it works on dumb terminals too).
  • Init() now returns tea.Batch(textinput.Blink, m.spinner.Tick).
  • Update() forwards every message to the spinner and batches the spinner's Tick cmd into the returned cmd, so the spinner animates regardless of which branch fires.
  • viewQRLogin now renders m.spinner.View() + " Generating QR code..." while loading (both before and after qrData is known).
  • The qrGeneratedMsg handler now clears m.message and m.messageStyle so the leftover banner doesn't appear under the rendered QR.

Tests

New tui/tui_qr_e2e_test.go:

  • TestQRFlowsThroughLoginMenuEndToEnd — drives the full user flow: pick the QR menu item (cursor=1, no-profiles branch), wait for the initQR cmd, deliver the qrGeneratedMsg, drain a few ticks, and assert the rendered View does not contain the word "Generating". This is the symptom the user reported.
  • Subtest loading state shows spinner — delivers a spinner.TickMsg before the URI arrives and asserts the loading view contains an active spinner frame char (| / / / - / \) along with the "Generating" text. Catches any regression that swaps the spinner back out for static text.

Local verification: go test -count=1 ./... passes. The end-to-end View dump no longer shows "Generating QR code..." after the QR arrives.

Files

  • tui/tui.go — spinner wiring, message clearing, animated loading view.
  • tui/tui_qr_e2e_test.go — new tests.
  • go.mod / go.sum — go mod tidy promoted bubbles, bubbletea, lipgloss, go-runewidth, qrterminal, and modernc.org/sqlite from indirect to direct deps (they're now directly imported); no new versions.

PR #66 handled the QR rendering width. This PR handles the loading-state UX around it.

The 'Scan with Amber' screen said 'Generating QR code...' both while
loading AND after the QR appeared — two bugs that together read as a
stuck screen with no progress indication.

Bug 1: m.message was set to 'Generating QR code...' in
handleLoginEnter and never cleared when qrGeneratedMsg arrived. View()
always appends m.message if non-empty, so the QR rendered correctly
above a contradictory 'still generating' banner. On terminals too
short to show both, the user only saw the 'still generating' line
and concluded the program was hung.

Bug 2: 'Generating...' was static text. With no animation the user
had no way to tell the program was doing work — it looked frozen.

Fix:
- Add a spinner.Model field on the TUI Model, initialized with the
  ASCII-only Line frame set so it renders even on dumb terminals.
- Init() now batches textinput.Blink with the spinner tick.
- Update forwards every message to the spinner and batches the
  resulting tick cmd into the returned cmd so the spinner animates
  regardless of which Update branch runs.
- viewQRLogin now renders 'spinner.View() + Generating QR code...'
  while loading, both before AND after the URI is known but the
  rendered QR isn't available yet.
- The qrGeneratedMsg handler clears m.message (and the message
  style) so the leftover banner doesn't appear under the rendered
  QR.

Tests:
- New TestQRFlowsThroughLoginMenuEndToEnd drives the full path a
  user takes: pick the QR menu item, wait for initQR, deliver the
  qrGeneratedMsg, and assert the View no longer contains the word
  'Generating'.
- Subtest 'loading state shows spinner' pumps a TickMsg and asserts
  the loading view contains an active spinner frame character.

Verified locally: 'go test -count=1 ./...' passes; the e2e test
captures a View where the QR is followed by the 'Waiting for
connection...' footer (no stale Generating text).

go.mod / go.sum: go mod tidy promoted bubbles, bubbletea, lipgloss,
go-runewidth, qrterminal, and modernc.org/sqlite from indirect to
direct deps since this change imports them as such. No new versions.
@oth-body
oth-body merged commit a16a382 into master Sep 14, 2026
7 checks passed
@oth-body
oth-body deleted the fix/qr-loading-state branch September 14, 2026 21:42
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