Skip to content

fix(desktop): eliminate 10+ second UI freeze on startup#361

Merged
wesbillman merged 5 commits into
mainfrom
improve-startup
Apr 18, 2026
Merged

fix(desktop): eliminate 10+ second UI freeze on startup#361
wesbillman merged 5 commits into
mainfrom
improve-startup

Conversation

@wesbillman
Copy link
Copy Markdown
Collaborator

Summary

  • Batch N per-channel relay mention subscriptions into a single multi-filter REQ (NIP-01), eliminating N×250ms EOSE waits
  • Phase non-critical startup work (presence, notifications, unread tracking) behind requestIdleCallback so the shell stays interactive
  • Memoize sidebar channel filter computations
  • Defer HomeView cascading queries (contacts → timeline → profiles) until shell is interactive

Test plan

  • Launch desktop app — shell should render and remain responsive immediately
  • Sidebar channels, forums, and DMs should appear without delay
  • Unread badges, presence dots, and home feed notifications should appear within ~2s (deferred but not missing)
  • Opening a DM before idle timeout fires should immediately load DM metadata (no 400ms wait)
  • Navigate away from Home and back — Pulse data should not flash/disappear (module-level flag prevents re-deferral)
  • Verify mention events still arrive in real-time after startup deferral completes

🤖 Generated with Claude Code

wesbillman and others added 5 commits April 18, 2026 08:41
Batch N per-channel relay mention subscriptions into a single multi-filter
REQ (NIP-01), phase non-critical startup work behind requestIdleCallback,
memoize sidebar channel filters, and defer HomeView cascading queries.

- Add subscribeToBatchedMentionEvents() to RelayClient — sends one REQ
  with N filters instead of N separate subscriptions, eliminating N×250ms
  EOSE waits
- Create shared useDeferredLoad/useDeferredStartup hooks to gate Tier 2
  initialization (presence, notifications, unread tracking) until the
  shell is interactive
- Wrap AppSidebar .filter() calls in useMemo
- Gate HomeView pulse queries behind deferred startup flag
- Add module-level flag so startup deferral only happens once per app
  lifecycle (no re-defer on route navigation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n tests)

The mention subscription must start immediately to catch real-time mentions
for sidebar badge updates. The batching already solved the N×250ms perf
problem — deferring the subscription itself caused E2E test failures where
mentions during the deferral window were missed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dge)

The home feed query must use the real pubkey immediately so that when
onLiveMention triggers a refetch, the query is enabled and can update
the sidebar badge count. Deferring it caused the badge to never appear
because refetch() on a disabled query is a no-op.

Only usePresenceSession remains deferred — everything else that powers
real-time mention detection and badge updates runs immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The batched mention subscription sends a single REQ with N filters
(one per channel), but the mock bridge only read the first filter to
extract the channel ID. Mentions in other channels were silently
dropped, breaking the live mention E2E tests.

Now collects channel IDs from all filters. Single-channel subs store
that ID; multi-channel subs store the "*" wildcard (app-side
handleMentionEvent already filters by pubkey).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The batched approach (one REQ with N filters) is incompatible with the
relay's fan-out architecture: multi-channel subscriptions get registered
as global (channel_id = None), and fan_out() enforces strict scoping
where global subscriptions never receive channel-scoped events. This
silently dropped all mention events.

Restores per-channel subscribeToChannelMentionEvents which registers
each subscription with a specific channel_id, ensuring fan_out() indexes
and delivers mention events correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wesbillman wesbillman merged commit fe93ac0 into main Apr 18, 2026
10 checks passed
@wesbillman wesbillman deleted the improve-startup branch April 18, 2026 17:06
fsola-sq added a commit that referenced this pull request Apr 20, 2026
…-binding

* origin/main:
  fix(desktop): eliminate agent startup beachball (#374)
  fix(desktop): resolve agent command path for DMG builds (#372)
  fix(desktop): remove stale sprout-admin prereq, add sidecar tooling (#371)
  Add server cross-compile and macOS desktop build CI jobs (#369)
  Fix forum post card bugs on desktop and mobile (#370)
  fix(desktop): kill WebSocket flood and fix Markdown <p><div> nesting (#368)
  perf: caching, batched DM resolution, bounded audit, global kind index (#367)
  fix: staging to generate stubs as needed (#366)
  chore(deps): update rust crate axum to v0.8.9 (#365)
  chore(deps): update dependency @tanstack/react-router to v1.168.22 (#364)
  feat(desktop): autoscroll thread sidebar for new replies (#363)
  fix(desktop): eliminate 10+ second UI freeze on startup (#361)
  feat(desktop): bundle sprout-acp and sprout-mcp-server as Tauri sidecars (#362)
  Remove release pipeline from public repo (#360)

Amp-Thread-ID: https://ampcode.com/threads/T-019dab7a-5979-7401-83a1-509b9adfe4a0
Co-authored-by: Amp <amp@ampcode.com>

# Conflicts:
#	crates/sprout-relay/src/state.rs
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