Skip to content

fix: auto-fetch profile from network when no cached data exists#763

Open
thepastaclaw wants to merge 1 commit into
dashpay:v1.0-devfrom
thepastaclaw:fix/auto-load-profile-on-wallet-import
Open

fix: auto-fetch profile from network when no cached data exists#763
thepastaclaw wants to merge 1 commit into
dashpay:v1.0-devfrom
thepastaclaw:fix/auto-load-profile-on-wallet-import

Conversation

@thepastaclaw
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw commented Mar 15, 2026

Issue

Closes #759 — After importing a wallet with an identity, the My DashPay Profile screen shows "No Profile Loaded" and requires a manual Refresh click.

Root Cause

ProfileScreen::load_profile_from_database() does nothing in the Ok(None) case (no cached profile in local SQLite). It leaves profile_load_attempted = false but doesn't trigger a network fetch, so the user sees a blank screen until they manually click Refresh.

Fix

When no cached profile data exists (Ok(None) branch), queue an automatic network fetch via trigger_load_profile() using the existing pending_action mechanism. This is safe from infinite loops because trigger_load_profile() sets profile_load_attempted = true, and load_profile_from_database() is only called when !profile_load_attempted.

Platform Dependency Update

Updated dashpay/platform and rust-dashcore to latest revisions and fixed three breaking API changes:

  • FeeLevel enum removed → use FeeRate::normal() static constructor
  • set_fee_level() removed → use set_fee_rate()
  • NetworkExt trait removed → methods now inherent on Network

Testing

  • cargo build — clean
  • cargo clippy --all-features --all-targets -- -D warnings — clean

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

  • Bug Fixes
    • Profile screen now automatically fetches profile data from the network when the cached profile is unavailable, eliminating the need for manual refresh. This improvement streamlines the loading experience and ensures users have timely access to their current profile information without requiring manual intervention.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 09a59cd1-c43b-4f3f-bf8a-d9a4c8338fd1

📥 Commits

Reviewing files that changed from the base of the PR and between 4181813 and 129c47d.

📒 Files selected for processing (1)
  • src/ui/dashpay/profile_screen.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ui/dashpay/profile_screen.rs

📝 Walkthrough

Walkthrough

The change adds automatic profile loading when no cached profile exists. Previously, load_profile_from_database would only log when cache missed; now it proactively triggers a network fetch via trigger_load_profile() and updates state accordingly.

Changes

Cohort / File(s) Summary
Profile Auto-Load on Cache Miss
src/ui/dashpay/profile_screen.rs
Modified load_profile_from_database to automatically trigger trigger_load_profile() and set profile_load_attempted = true when no cached profile exists, enabling auto-fetch from network instead of requiring manual user action.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A profile cached? Or missing quite?
No need to wait, no need to fight—
Auto-fetch now saves the day,
Loading fast in every way! 🥕
Identity appears, hip-hooray!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Out of Scope Changes check ❓ Inconclusive The PR description mentions platform dependency updates (dashpay/platform, rust-dashcore) and API breaking changes, but the raw_summary only shows changes to profile_screen.rs without documenting these dependency updates. Clarify whether dependency updates were applied in this PR or are planned separately; the raw_summary does not reflect these mentioned platform changes.
✅ Passed checks (3 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 change: auto-fetching profiles from network when no cached data exists, which directly addresses the core fix in the PR.
Linked Issues check ✅ Passed The code changes directly address issue #759 by automatically triggering profile network fetch when no cached data exists, eliminating the need for manual user action.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@lklimek lklimek marked this pull request as ready for review March 15, 2026 20:47
@lklimek lklimek requested a review from Copilot March 15, 2026 21:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes the “No Profile Loaded” state after importing a wallet by automatically triggering a network profile fetch when no cached profile exists in the local database.

Changes:

  • Enhance the Ok(None) database-load path to queue an automatic network fetch via pending_action.
  • Improve debug logging to indicate the auto-fetch behavior when cache is missing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ui/dashpay/profile_screen.rs Outdated
Copy link
Copy Markdown
Collaborator Author

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

The auto-fetch fix correctly queues a network load when no cached profile exists, and the loop-safety guard via profile_load_attempted + pending_action.is_none() is sound. The identity is properly captured in the backend task at queue time. However, the pre-existing lack of identity-matching in display_task_result() means the auto-fetch widens the window for a stale result to be applied to a different identity, and transient network failures are indistinguishable from a genuinely absent profile.

Reviewed commit: 4181813

🟡 3 suggestion(s) | 💬 1 nitpick(s)

1 additional finding

🟡 suggestion: Pre-existing: Err branch does not set profile_load_attempted, causing repeated error logs

src/ui/dashpay/profile_screen.rs (lines 336-338)

The Err(e) branch only logs the error without setting self.profile_load_attempted = true. Since refresh() (line 370–375) re-calls load_profile_from_database() every frame when profile.is_none() && !profile_load_attempted, a persistent DB error (e.g., schema mismatch after upgrade) will log on every frame indefinitely. This is pre-existing code not changed by the PR, but directly adjacent to the new Ok(None) handling and analogous in nature — the PR fixed the Ok(None) path for this exact symptom.

💡 Suggested change
                Err(e) => {
                    tracing::error!("Error loading profile from database: {}", e);
                    self.profile_load_attempted = true;
                }
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/ui/dashpay/profile_screen.rs`:
- [SUGGESTION] lines 321-334: Auto-fetch widens the window for stale profile results to land on the wrong identity
  The identity is correctly captured in `DashPayTask::LoadProfile { identity }` at line 333 (via `trigger_load_profile()` at line 344). However, `BackendTaskSuccessResult::DashPayProfile` carries no identity ID, so `display_task_result()` (line 1424) unconditionally applies the result to `self.selected_identity` — including DB cache writes at lines 1445–1493. This race is **pre-existing** (the manual Refresh button has the same vulnerability), but the auto-fetch meaningfully widens the window: the network round-trip can take seconds, during which the user may switch the identity selector (line 635), causing the old identity's profile to be displayed and cached under the new identity. The fix would be to include the source identity ID in `DashPayProfile` and skip application when it doesn't match `self.selected_identity`.
- [SUGGESTION] lines 328-333: Transient network failure renders as 'no profile exists'
  Line 332 sets `profile_load_attempted = true` before the network result arrives. If the fetch fails, `display_message()` (line 1416) clears `loading`/`saving` but does not reset `profile_load_attempted`. The render path at line 1250 then shows "This identity doesn't have a DashPay profile yet" — indistinguishable from a genuinely absent profile. This is the same behavior as the manual Refresh flow, but with auto-fetch the user never explicitly requested the load, so seeing a definitive "no profile" message after a silent network failure is more confusing. Consider resetting `profile_load_attempted` in `display_message()` on error so `refresh()` can re-attempt, or distinguishing "confirmed absent" from "fetch failed" in the UI.
- [SUGGESTION] lines 336-338: Pre-existing: Err branch does not set profile_load_attempted, causing repeated error logs
  The `Err(e)` branch only logs the error without setting `self.profile_load_attempted = true`. Since `refresh()` (line 370–375) re-calls `load_profile_from_database()` every frame when `profile.is_none() && !profile_load_attempted`, a persistent DB error (e.g., schema mismatch after upgrade) will log on every frame indefinitely. This is pre-existing code not changed by the PR, but directly adjacent to the new `Ok(None)` handling and analogous in nature — the PR fixed the `Ok(None)` path for this exact symptom.

Comment on lines 321 to +334
Ok(None) => {
tracing::debug!("No profile found in database for identity {}", identity_id);
tracing::debug!(
"No profile found in database for identity {}, will auto-fetch from network",
identity_id
);
// No cached profile exists — queue an automatic network fetch
// so the user doesn't have to manually click "Refresh".
if self.pending_action.is_none() {
// Mark that we've already attempted to load a profile to
// avoid repeatedly queuing the same work if this method
// is called again before the pending action executes.
self.profile_load_attempted = true;
self.pending_action = Some(Box::new(self.trigger_load_profile()));
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Auto-fetch widens the window for stale profile results to land on the wrong identity

The identity is correctly captured in DashPayTask::LoadProfile { identity } at line 333 (via trigger_load_profile() at line 344). However, BackendTaskSuccessResult::DashPayProfile carries no identity ID, so display_task_result() (line 1424) unconditionally applies the result to self.selected_identity — including DB cache writes at lines 1445–1493. This race is pre-existing (the manual Refresh button has the same vulnerability), but the auto-fetch meaningfully widens the window: the network round-trip can take seconds, during which the user may switch the identity selector (line 635), causing the old identity's profile to be displayed and cached under the new identity. The fix would be to include the source identity ID in DashPayProfile and skip application when it doesn't match self.selected_identity.

source: ['codex-general', 'codex-rust', 'claude-rust']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/ui/dashpay/profile_screen.rs`:
- [SUGGESTION] lines 321-334: Auto-fetch widens the window for stale profile results to land on the wrong identity
  The identity is correctly captured in `DashPayTask::LoadProfile { identity }` at line 333 (via `trigger_load_profile()` at line 344). However, `BackendTaskSuccessResult::DashPayProfile` carries no identity ID, so `display_task_result()` (line 1424) unconditionally applies the result to `self.selected_identity` — including DB cache writes at lines 1445–1493. This race is **pre-existing** (the manual Refresh button has the same vulnerability), but the auto-fetch meaningfully widens the window: the network round-trip can take seconds, during which the user may switch the identity selector (line 635), causing the old identity's profile to be displayed and cached under the new identity. The fix would be to include the source identity ID in `DashPayProfile` and skip application when it doesn't match `self.selected_identity`.

Comment on lines +328 to +333
if self.pending_action.is_none() {
// Mark that we've already attempted to load a profile to
// avoid repeatedly queuing the same work if this method
// is called again before the pending action executes.
self.profile_load_attempted = true;
self.pending_action = Some(Box::new(self.trigger_load_profile()));
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Transient network failure renders as 'no profile exists'

Line 332 sets profile_load_attempted = true before the network result arrives. If the fetch fails, display_message() (line 1416) clears loading/saving but does not reset profile_load_attempted. The render path at line 1250 then shows "This identity doesn't have a DashPay profile yet" — indistinguishable from a genuinely absent profile. This is the same behavior as the manual Refresh flow, but with auto-fetch the user never explicitly requested the load, so seeing a definitive "no profile" message after a silent network failure is more confusing. Consider resetting profile_load_attempted in display_message() on error so refresh() can re-attempt, or distinguishing "confirmed absent" from "fetch failed" in the UI.

source: ['codex-general', 'codex-rust']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/ui/dashpay/profile_screen.rs`:
- [SUGGESTION] lines 328-333: Transient network failure renders as 'no profile exists'
  Line 332 sets `profile_load_attempted = true` before the network result arrives. If the fetch fails, `display_message()` (line 1416) clears `loading`/`saving` but does not reset `profile_load_attempted`. The render path at line 1250 then shows "This identity doesn't have a DashPay profile yet" — indistinguishable from a genuinely absent profile. This is the same behavior as the manual Refresh flow, but with auto-fetch the user never explicitly requested the load, so seeing a definitive "no profile" message after a silent network failure is more confusing. Consider resetting `profile_load_attempted` in `display_message()` on error so `refresh()` can re-attempt, or distinguishing "confirmed absent" from "fetch failed" in the UI.

Comment on lines +332 to +333
self.profile_load_attempted = true;
self.pending_action = Some(Box::new(self.trigger_load_profile()));
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

💬 Nitpick: Redundant profile_load_attempted assignment

Line 332 sets self.profile_load_attempted = true before calling self.trigger_load_profile() on line 333, which also sets self.profile_load_attempted = true unconditionally (line 346). The guard at line 328 (pending_action.is_none()) already prevents re-entry, and load_profile_from_database() is inside if let Some(identity) = &self.selected_identity so the else branch of trigger_load_profile() cannot execute. The duplicate assignment is harmless but technically redundant.

source: ['claude-general', 'claude-rust']

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 19, 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.

When a wallet with an existing identity is imported, identity discovery stores the identity in the local DB but the DashPay profile is not cached. Previously, the Profile Screen would show 'No Profile Loaded' and require the user to manually click 'Refresh' to fetch from the network.

Now, when load_profile_from_database() finds no cached profile data, it automatically queues a network fetch via pending_action. The queueing is guarded with pending_action.is_none() and marks profile_load_attempted before the pending action executes so refresh() does not repeatedly enqueue the same work.

Fixes dashpay#759
@thepastaclaw thepastaclaw force-pushed the fix/auto-load-profile-on-wallet-import branch from 4181813 to 129c47d Compare April 14, 2026 04:02
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

thepastaclaw commented Apr 14, 2026

✅ Review complete (commit 129c47d)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

after imortting the wallet with identity, the identity does not appear on My DashPay Profile screen

3 participants