Skip to content

fix(ui): prevent 'No users found' flash during profile search#736

Closed
thepastaclaw wants to merge 4 commits into
dashpay:v1.0-devfrom
thepastaclaw:fix/684-profile-search-no-users-flash
Closed

fix(ui): prevent 'No users found' flash during profile search#736
thepastaclaw wants to merge 4 commits into
dashpay:v1.0-devfrom
thepastaclaw:fix/684-profile-search-no-users-flash

Conversation

@thepastaclaw
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw commented Mar 12, 2026

Issue

Fixes #684 — Search Public Profiles always shows "No users found" briefly before results load.

Root Cause

When a search starts, has_searched is set to true and results are cleared. The display_message() method (called for banner side-effects) sets loading = false before display_task_result() delivers actual results. This creates a frame where has_searched && !loading && results.is_empty() is true, causing the "No users found" message to flash.

Fix

Added a search_completed flag that's only set in display_task_result(), and changed the empty-results condition to check search_completed instead of has_searched. This ensures the "No users found" message only appears after the backend has actually returned (empty) results.

Testing

  • Start a profile search → spinner shows, no flash of "No users found"
  • Complete search with results → results display normally
  • Complete search with no matches → "No users found" displays (after results arrive)
  • Clear Results button → resets all state correctly

Summary by CodeRabbit

  • New Features

    • Real-time progress updates from long-running backend tasks are now shown in the UI and global refresh banner.
    • Search UI now tracks backend completion separately to drive loading and result visibility.
  • Bug Fixes

    • Prevented premature "No users found" message by showing no-results only after backend search completes.
    • Improved loading/reset behavior on search and clear to avoid misleading states.

thepastaclaw and others added 2 commits March 11, 2026 21:10
…pay#713)

Add BackendTaskSuccessResult::Progress variant for progress messages
that should update the existing banner instead of creating new global
banners. Change load_user_identities_up_to_index to send Progress
instead of Message for "Searching index X of Y..." updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When searching profiles, 'No users found' would briefly flash before
results loaded. This happened because display_message() set loading=false
(for error banner side-effects) before display_task_result() delivered
actual results, creating a frame where has_searched=true && !loading &&
results.is_empty().

Fix: Add search_completed flag that's only set in display_task_result(),
ensuring the empty-results message only appears after the backend has
actually responded.

Fixes dashpay#684
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

Adds a Progress(String) success variant and emits progress updates from identity-loading; the app now routes progress results to UI handlers. Two UI screens consume progress updates; ProfileSearch gains a search_completed flag to suppress premature "No users found" messaging.

Changes

Cohort / File(s) Summary
Backend enum
src/backend_task/mod.rs
Add public BackendTaskSuccessResult::Progress(String) variant; adjust Message comment.
Progress emission
src/backend_task/identity/load_identity_from_wallet.rs
Emit BackendTaskSuccessResult::Progress(...) for per-index updates instead of Message(...).
App task handling
src/app.rs
Handle BackendTaskSuccessResult::Progress(_) in TaskResult::Success, forwarding progress to display_task_result.
UI: identities screen
src/ui/identities/add_existing_identity_screen.rs
Handle BackendTaskSuccessResult::Progress in display_task_result to update global refresh/banner progress message.
UI: profile search
src/ui/dashpay/profile_search.rs
Add search_completed: bool to ProfileSearchScreen; reset on new searches/clear, set on task completion, and show "No users found" only after search_completed is true.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(200,220,255,0.5)
    participant User
    participant UI as "UI Screen\n(ProfileSearch / AddExisting)"
  end
  rect rgba(200,255,200,0.5)
    participant App
  end
  rect rgba(255,220,200,0.5)
    participant Backend as "Backend Task\n(load identities / search)"
  end

  User->>UI: initiate search / start action
  UI->>App: dispatch task request
  App->>Backend: start backend task
  Backend-->>App: BackendTaskSuccessResult::Progress(msg)
  App-->>UI: display_task_result(progress msg)
  UI->>User: update progress banner / indicator
  Backend-->>App: BackendTaskSuccessResult::Message|Result(end)
  App-->>UI: display_task_result(final result)
  UI->>User: show results or "No users found" (after search_completed)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
Progress hops along the trail,
Little notes in every mail,
Search waits patient, then reveals,
No more empty "none" that feels—
I nibble bugs and cheer the wheels. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix—preventing the 'No users found' flash during profile search, which aligns with the primary changeset focus on UI state management.
Linked Issues check ✅ Passed The PR fully addresses issue #684 by introducing a search_completed flag to prevent the transient 'No users found' message and ensuring it only appears after backend results return.
Out of Scope Changes check ✅ Passed All changes directly support the fix for issue #684: backend task result handling for progress updates, UI state flag management, and loading state lifecycle—no extraneous changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/ui/dashpay/profile_search.rs (1)

324-326: Consider guarding search_completed assignment.

Setting search_completed = true unconditionally at the start of display_task_result means any backend result (including unrelated ones caught by the wildcard arm) would mark the search as completed. While this works correctly now since profile search only receives DashPayProfileSearchResults, a defensive approach would be to set it only for the expected result type.

🔧 Suggested refinement
     fn display_task_result(&mut self, result: BackendTaskSuccessResult) {
         self.loading = false;
-        self.search_completed = true;

         match result {
             BackendTaskSuccessResult::DashPayProfileSearchResults(results) => {
+                self.search_completed = true;
                 self.search_results.clear();
                 // ... rest of the handler
             }
             BackendTaskSuccessResult::Message(_msg) => {
                 // Message display is handled globally by AppState
             }
             _ => {
                 // Ignore other results
             }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/dashpay/profile_search.rs` around lines 324 - 326, The method
display_task_result currently sets self.search_completed = true unconditionally;
change it so search_completed is only set to true when the backend result is the
expected DashPayProfileSearchResults variant (i.e., inside the match arm that
handles BackendTaskSuccessResult::DashPayProfileSearchResults). Keep
self.loading = false as appropriate, but move the search_completed assignment
into the specific match arm for DashPayProfileSearchResults (and do not set it
in the wildcard/unrelated arms) to avoid marking searches completed for
unrelated backend results.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/dashpay/profile_search.rs`:
- Around line 324-326: The method display_task_result currently sets
self.search_completed = true unconditionally; change it so search_completed is
only set to true when the backend result is the expected
DashPayProfileSearchResults variant (i.e., inside the match arm that handles
BackendTaskSuccessResult::DashPayProfileSearchResults). Keep self.loading =
false as appropriate, but move the search_completed assignment into the specific
match arm for DashPayProfileSearchResults (and do not set it in the
wildcard/unrelated arms) to avoid marking searches completed for unrelated
backend results.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bdea49ce-8008-4d75-8216-ca8921668df8

📥 Commits

Reviewing files that changed from the base of the PR and between 91abb2c and 6e247d0.

📒 Files selected for processing (5)
  • src/app.rs
  • src/backend_task/identity/load_identity_from_wallet.rs
  • src/backend_task/mod.rs
  • src/ui/dashpay/profile_search.rs
  • src/ui/identities/add_existing_identity_screen.rs

…s arm

Move self.loading = false and self.search_completed = true inside the
DashPayProfileSearchResults match arm instead of setting them unconditionally
at the top of display_task_result.

Without this fix, any unrelated BackendTaskSuccessResult (e.g. Progress
messages from a concurrent identity-load task) routed to the visible screen
could prematurely clear the loading spinner and set search_completed = true,
causing the 'No users found' flash that this PR set out to fix.

Addresses CodeRabbit review comment.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/dashpay/profile_search.rs (1)

256-259: ⚠️ Potential issue | 🟠 Major

Do not clear loading from generic display_message() callbacks.

Line 258 still sets self.loading = false for any global message callback, which can hide the spinner before DashPayProfileSearchResults arrives (e.g., unrelated concurrent banner events).

Suggested fix
 pub fn display_message(&mut self, _message: &str, _message_type: MessageType) {
     // Banner display is handled globally by AppState; this is only for side-effects.
-    self.loading = false;
+    // Keep loading state tied to the profile-search task lifecycle in `display_task_result()`.
 }

If failure-path cleanup is needed, handle that in display_task_error() for this screen/task instead of in generic banner callbacks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/dashpay/profile_search.rs` around lines 256 - 259, The generic
callback display_message currently clears self.loading unconditionally; remove
the assignment to self.loading = false from the display_message method so banner
events won't hide the spinner for unrelated messages, and move any failure-path
cleanup to the screen-specific error handler display_task_error (operate on
self.loading there) or wherever DashPayProfileSearchResults handles task
completion/failure so loading is only cleared for this task's terminal states.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/ui/dashpay/profile_search.rs`:
- Around line 256-259: The generic callback display_message currently clears
self.loading unconditionally; remove the assignment to self.loading = false from
the display_message method so banner events won't hide the spinner for unrelated
messages, and move any failure-path cleanup to the screen-specific error handler
display_task_error (operate on self.loading there) or wherever
DashPayProfileSearchResults handles task completion/failure so loading is only
cleared for this task's terminal states.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 75f23417-b539-4216-af9d-14bf69447bf6

📥 Commits

Reviewing files that changed from the base of the PR and between 6e247d0 and a7fc790.

📒 Files selected for processing (1)
  • src/ui/dashpay/profile_search.rs

Address CodeRabbit review: remove self.loading = false from the generic
display_message() callback, which fires for ANY completed backend task
and could hide the spinner before search results arrive.

Loading is now only cleared via:
- display_task_result: inside the DashPayProfileSearchResults arm
- display_task_error (new override): on task failure
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/ui/dashpay/profile_search.rs (1)

257-264: Align backend-task banner lifecycle with screen guidelines.

This no-op display_message() path leaves out the expected refresh_banner handle lifecycle (set_global on dispatch, take_and_clear() on completion/error).

As per coding guidelines "Screens with backend tasks should store a refresh_banner: Option<BannerHandle> field; on task dispatch set it via MessageBanner::set_global() with info/progress, then in display_message() dismiss it via take_and_clear() (not just setting to None)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/dashpay/profile_search.rs` around lines 257 - 264, The screen
currently leaves display_message() as a no-op and therefore skips properly
dismissing the banner lifecycle; add a refresh_banner: Option<BannerHandle>
field on the profile-search screen (if not present), ensure the task dispatch
path uses MessageBanner::set_global(...) to set refresh_banner on dispatch, and
modify display_message(&mut self, _message: &str, _message_type: MessageType) to
call self.refresh_banner.take_and_clear() (or call take_and_clear() on the
stored BannerHandle) to dismiss the banner on completion/error rather than just
setting the field to None so the spinner/banner lifecycle aligns with the
guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/dashpay/profile_search.rs`:
- Around line 257-264: The screen currently leaves display_message() as a no-op
and therefore skips properly dismissing the banner lifecycle; add a
refresh_banner: Option<BannerHandle> field on the profile-search screen (if not
present), ensure the task dispatch path uses MessageBanner::set_global(...) to
set refresh_banner on dispatch, and modify display_message(&mut self, _message:
&str, _message_type: MessageType) to call self.refresh_banner.take_and_clear()
(or call take_and_clear() on the stored BannerHandle) to dismiss the banner on
completion/error rather than just setting the field to None so the
spinner/banner lifecycle aligns with the guidelines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1df019e0-d991-4f14-928f-10226bcb3fce

📥 Commits

Reviewing files that changed from the base of the PR and between a7fc790 and 519c9a3.

📒 Files selected for processing (1)
  • src/ui/dashpay/profile_search.rs

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Closing as duplicate of #748, which has the more complete fix. Apologies for the noise — this was caused by a heartbeat loop that didn't check for existing PRs before re-investigating the same issue.

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.

Search Public Profiles always show "No users found" at the beginning

1 participant