Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b29b23a
fix: connection status not updated
lklimek Feb 5, 2026
852135e
chore: rabbit feedback
lklimek Feb 5, 2026
c0ee391
Initial plan
Copilot Feb 6, 2026
5771276
Track DAPI connection status and display in tooltips and connection info
Copilot Feb 6, 2026
3ab7413
Address review feedback: store available endpoint count and extract D…
Copilot Feb 6, 2026
3f6b0c8
Fix rustfmt formatting issues in connection_status.rs
Copilot Feb 6, 2026
a7c7ea4
Fix borrow checker error: extract DAPI status values before mutable s…
Copilot Feb 6, 2026
f509484
Switch Platform to c2c88e4 and use get_live_addresses() for accurate …
Copilot Feb 9, 2026
e3e363b
Merge remote-tracking branch 'origin/v1.0-dev' into fix/conn-status
lklimek Feb 9, 2026
bf49217
chore: typo + network changes
lklimek Feb 9, 2026
6a7251c
chore: apply feedback
lklimek Feb 9, 2026
5a07e24
chore: rabbit review
lklimek Feb 9, 2026
398dd74
chore: rabbit feedback
lklimek Feb 9, 2026
158f53e
chore: rabbitting
lklimek Feb 9, 2026
dec9ece
Merge branch 'fix/conn-status' of https://github.com/dashpay/dash-evo…
Copilot Feb 10, 2026
4048cec
Remove overall_connected_with method (deleted upstream in base branch)
Copilot Feb 10, 2026
5acbaf3
Merge v1.0-dev into copilot/update-dapi-connection-status
Copilot Feb 10, 2026
44ad54b
Merge origin/v1.0-dev into copilot/update-dapi-connection-status
lklimek Feb 10, 2026
4eb2e2e
chore: fix platfom versioning issues
lklimek Feb 10, 2026
b417bce
chore: add todo
lklimek Feb 10, 2026
777c715
chore: fmt
lklimek Feb 10, 2026
408af18
Merge remote-tracking branch 'origin/v1.0-dev' into copilot/update-da…
lklimek Feb 12, 2026
90a02fd
Merge branch 'v1.0-dev' into copilot/update-dapi-connection-status
lklimek Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 110 additions & 102 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,14 +789,14 @@ impl App for AppState {
// Apply Dash theme with user preference
crate::ui::theme::apply_theme(ctx, self.theme_preference);

if let Ok(event) = self.current_app_context().rx_zmq_status.try_recv()
&& let Ok(mut status) = self.current_app_context().zmq_connection_status.lock()
{
*status = event;
}
let active_context = self.current_app_context().clone();

// Poll the receiver for any new task results
while let Ok(task_result) = self.task_result_receiver.try_recv() {
active_context
.connection_status()
.handle_task_result(&task_result, active_context.network);

// Handle the result on the main thread
match task_result {
TaskResult::Success(message) => {
Expand Down Expand Up @@ -984,112 +984,120 @@ impl App for AppState {
}

// Show welcome screen if onboarding not completed
let action = if self.show_welcome_screen {
if let Some(welcome_screen) = &mut self.welcome_screen {
welcome_screen.ui(ctx)
} else {
AppAction::None
}
let mut actions = Vec::new();
if self.show_welcome_screen
&& let Some(welcome_screen) = &mut self.welcome_screen
{
actions.push(welcome_screen.ui(ctx));
} else {
self.visible_screen_mut().ui(ctx)
actions.push(self.visible_screen_mut().ui(ctx));
};

match action {
AppAction::AddScreen(screen) => self.screen_stack.push(screen),
AppAction::None => {}
AppAction::Refresh => self.visible_screen_mut().refresh(),
AppAction::PopScreen => {
if !self.screen_stack.is_empty() {
self.screen_stack.pop();
// Schedule connection status refresh
actions.push(
active_context
.connection_status()
.trigger_refresh(active_context.as_ref()),
);

for action in actions {
match action {
AppAction::None => {}
AppAction::AddScreen(screen) => self.screen_stack.push(screen),
AppAction::Refresh => self.visible_screen_mut().refresh(),
AppAction::PopScreen => {
if !self.screen_stack.is_empty() {
self.screen_stack.pop();
}
}
}
AppAction::PopScreenAndRefresh => {
if !self.screen_stack.is_empty() {
self.screen_stack.pop();
AppAction::PopScreenAndRefresh => {
if !self.screen_stack.is_empty() {
self.screen_stack.pop();
}
if let Some(screen) = self.screen_stack.last_mut() {
screen.refresh();
} else {
self.active_root_screen_mut().refresh_on_arrival();
}
}
if let Some(screen) = self.screen_stack.last_mut() {
screen.refresh();
} else {
AppAction::GoToMainScreen => {
self.screen_stack = vec![];
self.active_root_screen_mut().refresh_on_arrival();
}
}
AppAction::GoToMainScreen => {
self.screen_stack = vec![];
self.active_root_screen_mut().refresh_on_arrival();
}
AppAction::BackendTask(task) => {
self.handle_backend_task(task);
}
AppAction::BackendTasks(tasks, mode) => {
self.handle_backend_tasks(tasks, mode);
}
AppAction::SetMainScreen(root_screen_type) => {
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
}
AppAction::SetMainScreenThenGoToMainScreen(root_screen_type) => {
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
self.screen_stack = vec![];
}
AppAction::SetMainScreenThenPopScreen(root_screen_type) => {
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
if !self.screen_stack.is_empty() {
self.screen_stack.pop();
AppAction::BackendTask(task) => {
self.handle_backend_task(task);
}
}
AppAction::SwitchNetwork(network) => {
self.change_network(network);
self.current_app_context()
.update_settings(RootScreenType::RootScreenNetworkChooser)
.ok();
}
AppAction::PopThenAddScreenToMainScreen(root_screen_type, screen) => {
self.screen_stack = vec![screen];
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
}
AppAction::Custom(_) => {}
AppAction::OnboardingComplete {
main_screen,
add_screen,
} => {
self.show_welcome_screen = false;
self.welcome_screen = None;
self.selected_main_screen = main_screen;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context().update_settings(main_screen).ok();
// If there's an additional screen to push, create and push it
if let Some(screen_type) = add_screen {
let screen = screen_type.create_screen(self.current_app_context());
self.screen_stack.push(screen);
AppAction::BackendTasks(tasks, mode) => {
self.handle_backend_tasks(tasks, mode);
}
// Start SPV sync after onboarding completes (if auto-start is enabled and developer mode is on)
// TODO: SPV auto-start is gated behind developer mode while SPV is in development.
// Remove the is_developer_mode() check once SPV is production-ready.
let current_context = self.current_app_context();
let auto_start_spv = current_context.db.get_auto_start_spv().unwrap_or(false);
if auto_start_spv
&& current_context.is_developer_mode()
&& current_context.core_backend_mode() == crate::spv::CoreBackendMode::Spv
{
if let Err(e) = current_context.start_spv() {
tracing::warn!("Failed to start SPV sync after onboarding: {}", e);
} else {
tracing::info!("SPV sync started after onboarding");
AppAction::SetMainScreen(root_screen_type) => {
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
}
AppAction::SetMainScreenThenGoToMainScreen(root_screen_type) => {
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
self.screen_stack = vec![];
}
AppAction::SetMainScreenThenPopScreen(root_screen_type) => {
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
if !self.screen_stack.is_empty() {
self.screen_stack.pop();
}
}
AppAction::SwitchNetwork(network) => {
self.change_network(network);
self.current_app_context()
.update_settings(RootScreenType::RootScreenNetworkChooser)
.ok();
}
AppAction::PopThenAddScreenToMainScreen(root_screen_type, screen) => {
self.screen_stack = vec![screen];
self.selected_main_screen = root_screen_type;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context()
.update_settings(root_screen_type)
.ok();
}
AppAction::Custom(_) => {}
AppAction::OnboardingComplete {
main_screen,
add_screen,
} => {
self.show_welcome_screen = false;
self.welcome_screen = None;
self.selected_main_screen = main_screen;
self.active_root_screen_mut().refresh_on_arrival();
self.current_app_context().update_settings(main_screen).ok();
// If there's an additional screen to push, create and push it
if let Some(screen_type) = add_screen {
let screen = screen_type.create_screen(self.current_app_context());
self.screen_stack.push(screen);
}
// Start SPV sync after onboarding completes (if auto-start is enabled and developer mode is on)
// TODO: SPV auto-start is gated behind developer mode while SPV is in development.
// Remove the is_developer_mode() check once SPV is production-ready.
let current_context = self.current_app_context();
let auto_start_spv = current_context.db.get_auto_start_spv().unwrap_or(false);
if auto_start_spv
&& current_context.is_developer_mode()
&& current_context.core_backend_mode() == crate::spv::CoreBackendMode::Spv
{
if let Err(e) = current_context.start_spv() {
tracing::warn!("Failed to start SPV sync after onboarding: {}", e);
} else {
tracing::info!("SPV sync started after onboarding");
}
}
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::app_dir::core_cookie_path;
use crate::backend_task::contested_names::ScheduledDPNSVote;
use crate::components::core_zmq_listener::ZMQConnectionEvent;
pub mod connection_status;
use crate::config::{Config, NetworkConfig};
use crate::context_provider::Provider as RpcProvider;
use crate::context_provider_spv::SpvProvider;
Expand All @@ -22,6 +23,7 @@ use crate::ui::RootScreenType;
use crate::ui::tokens::tokens_screen::{IdentityTokenBalance, IdentityTokenIdentifier};
use crate::utils::tasks::TaskManager;
use bincode::config;
use connection_status::ConnectionStatus;
use crossbeam_channel::{Receiver, Sender};
use dash_sdk::Sdk;
use dash_sdk::dashcore_rpc::dashcore::{InstantLock, Transaction};
Expand Down Expand Up @@ -75,7 +77,6 @@ pub struct AppContext {
pub(crate) config: Arc<RwLock<NetworkConfig>>,
pub(crate) rx_zmq_status: Receiver<ZMQConnectionEvent>,
pub(crate) sx_zmq_status: Sender<ZMQConnectionEvent>,
pub(crate) zmq_connection_status: Mutex<ZMQConnectionEvent>,
pub(crate) dpns_contract: Arc<DataContract>,
pub(crate) withdraws_contract: Arc<DataContract>,
pub(crate) dashpay_contract: Arc<DataContract>,
Expand All @@ -100,6 +101,7 @@ pub struct AppContext {
pub(crate) subtasks: Arc<TaskManager>,
pub(crate) spv_manager: Arc<SpvManager>,
core_backend_mode: AtomicU8,
pub(crate) connection_status: ConnectionStatus,
/// Pending wallet selection - set after creating/importing a wallet
/// so the wallet screen can auto-select the new wallet
pub(crate) pending_wallet_selection: Mutex<Option<WalletSeedHash>>,
Expand Down Expand Up @@ -270,12 +272,12 @@ impl AppContext {
single_key_wallets: RwLock::new(single_key_wallets),
password_info,
transactions_waiting_for_finality: Mutex::new(BTreeMap::new()),
zmq_connection_status: Mutex::new(ZMQConnectionEvent::Disconnected),
animate,
cached_settings: RwLock::new(None),
subtasks,
spv_manager,
core_backend_mode: AtomicU8::new(saved_core_backend_mode),
connection_status: ConnectionStatus::new(),
pending_wallet_selection: Mutex::new(None),
selected_wallet_hash: Mutex::new(selected_wallet_hash),
selected_single_key_hash: Mutex::new(selected_single_key_hash),
Expand Down Expand Up @@ -349,6 +351,10 @@ impl AppContext {
self.core_backend_mode.load(Ordering::Relaxed).into()
}

pub fn connection_status(&self) -> &ConnectionStatus {
&self.connection_status
}

pub fn set_core_backend_mode(self: &Arc<Self>, mode: CoreBackendMode) {
self.core_backend_mode
.store(mode.as_u8(), Ordering::Relaxed);
Expand Down
Loading
Loading