diff --git a/crates/tauri-app/src/lib.rs b/crates/tauri-app/src/lib.rs
index 8cac71c..5268ac8 100644
--- a/crates/tauri-app/src/lib.rs
+++ b/crates/tauri-app/src/lib.rs
@@ -2497,6 +2497,34 @@ async fn export_data(
paths.join(", ")))
}
+/// Clear the duplicate identity flag and start sync tasks that were skipped.
+#[tauri::command]
+async fn clear_duplicate_flag(state: State<'_, AppNode>) -> Result<(), String> {
+ let node = get_node(&state).await;
+ node.network.duplicate_detected.store(false, std::sync::atomic::Ordering::Relaxed);
+ // Start the sync tasks that were skipped during bootstrap
+ node.start_accept_loop();
+ node.start_pull_cycle(300);
+ node.start_diff_cycle(120);
+ node.start_rebalance_cycle(600);
+ node.start_growth_loop();
+ node.start_recovery_loop();
+ node.start_social_checkin_cycle(3600);
+ node.start_anchor_register_cycle(600);
+ node.start_upnp_renewal_cycle();
+ node.start_upnp_tcp_renewal_cycle();
+ node.start_http_server();
+ node.start_bootstrap_connectivity_check();
+ node.start_replication_cycle(600);
+ let cache_max_bytes: u64 = {
+ let storage = node.storage.get().await;
+ storage.get_setting("cache_size_bytes").ok().flatten()
+ .and_then(|s| s.parse().ok()).unwrap_or(1_073_741_824u64)
+ };
+ Node::start_eviction_cycle(Arc::clone(&node), 300, cache_max_bytes);
+ Ok(())
+}
+
/// On Android: save a file from the app's internal storage to a user-chosen location via SAF.
/// On desktop: no-op (files are already in ~/Downloads).
#[tauri::command]
@@ -2803,6 +2831,7 @@ pub fn run() {
get_active_identity,
export_data,
share_file,
+ clear_duplicate_flag,
import_summary,
import_public_posts,
import_as_new_identity,
diff --git a/frontend/app.js b/frontend/app.js
index 00af266..697e6f8 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -3918,10 +3918,14 @@ async function init() {
if (info && info.duplicateDetected) {
_duplicateWarningShown = true;
const banner = document.createElement('div');
- banner.style.cssText = 'position:fixed;top:0;left:0;right:0;background:#c0392b;color:#fff;padding:0.5rem;text-align:center;font-size:0.8rem;z-index:999;';
- banner.textContent = 'This identity is active on another device. Sync paused to prevent data conflicts.';
+ banner.style.cssText = 'position:fixed;top:0;left:0;right:0;background:#c0392b;color:#fff;padding:0.5rem;text-align:center;font-size:0.8rem;z-index:999;display:flex;align-items:center;justify-content:center;gap:0.5rem;';
+ banner.innerHTML = 'This identity may be active on another device. Sync paused.';
document.body.prepend(banner);
- toast('Duplicate identity detected — sync paused');
+ banner.querySelector('#dup-override-btn').addEventListener('click', async () => {
+ try { await invoke('clear_duplicate_flag'); } catch (_) {}
+ banner.remove();
+ toast('Sync resumed');
+ });
}
} catch (_) {}
}