From 6ca0d9f015fc12058aa320b664e12d1b05acf134 Mon Sep 17 00:00:00 2001 From: Owen McGirr Date: Sat, 22 Aug 2026 11:14:35 +0100 Subject: [PATCH 1/2] fix: record device connection time --- src-tauri/src/macos.rs | 12 ++- src-tauri/src/protocol.rs | 65 ++++++++++------ src-tauri/src/state.rs | 123 ++++++++++++++++++------------- src-tauri/src/windows_runtime.rs | 12 ++- src/App.test.tsx | 15 ++++ src/App.tsx | 2 +- 6 files changed, 147 insertions(+), 82 deletions(-) diff --git a/src-tauri/src/macos.rs b/src-tauri/src/macos.rs index 084c62d5..5e72d8dc 100644 --- a/src-tauri/src/macos.rs +++ b/src-tauri/src/macos.rs @@ -719,16 +719,20 @@ impl MacRuntime { self.report_error(error); } } - Ok(Some(EngineEvent::RemoteNameUpdate(update))) => { + Ok(Some(EngineEvent::AuthenticatedConnection(connection))) => { let model = self.app.state::(); let saved = model - .apply_remote_name(&update.device_id, &update.device_name) + .record_authenticated_connection( + &connection.device_id, + connection.device_name.as_deref(), + connection.connected_at, + ) .unwrap_or(false); if !saved { set_activity( &self.shared, ActivityKind::Error, - "The Remote name could not be saved.", + "The device connection details could not be saved.", ); } let response = self @@ -736,7 +740,7 @@ impl MacRuntime { .lock() .unwrap_or_else(|poisoned| poisoned.into_inner()) .engine - .complete_remote_name_update(&update, saved); + .complete_authenticated_connection(&connection, saved); if let Err(error) = self.enqueue_message(&response) { self.report_error(error); } diff --git a/src-tauri/src/protocol.rs b/src-tauri/src/protocol.rs index 21e8f9a8..ae339318 100644 --- a/src-tauri/src/protocol.rs +++ b/src-tauri/src/protocol.rs @@ -334,10 +334,11 @@ pub struct DesktopCommand { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct RemoteNameUpdate { +pub struct AuthenticatedConnection { pub id: String, pub device_id: String, - pub device_name: String, + pub device_name: Option, + pub connected_at: i64, } #[derive(Debug, Clone, PartialEq)] @@ -368,7 +369,7 @@ pub enum EngineEvent { replaced_response: Option, }, Response(String), - RemoteNameUpdate(RemoteNameUpdate), + AuthenticatedConnection(AuthenticatedConnection), PointerProfile(String), MouseMove(MouseMoveCommand), MouseClick(MouseClickCommand), @@ -557,12 +558,16 @@ impl ProtocolEngine { } } - pub fn complete_remote_name_update(&self, update: &RemoteNameUpdate, saved: bool) -> String { - if saved { - ack_response(&update.id) + pub fn complete_authenticated_connection( + &self, + connection: &AuthenticatedConnection, + saved: bool, + ) -> String { + if saved || connection.device_name.is_none() { + ack_response(&connection.id) } else { error_response( - Some(&update.id), + Some(&connection.id), "name_update_failed", "The Remote name could not be saved.", ) @@ -611,13 +616,23 @@ impl ProtocolEngine { match command_type { "connection.ping" => match validated.payload.get("deviceName") { - None => Ok(EngineEvent::Response(ack_response(&validated.id))), - Some(value) => match value.as_str().and_then(normalize_remote_name) { - Some(device_name) => Ok(EngineEvent::RemoteNameUpdate(RemoteNameUpdate { + None => Ok(EngineEvent::AuthenticatedConnection( + AuthenticatedConnection { id: validated.id, device_id: validated.device_id, - device_name, - })), + device_name: None, + connected_at: now_ms, + }, + )), + Some(value) => match value.as_str().and_then(normalize_remote_name) { + Some(device_name) => Ok(EngineEvent::AuthenticatedConnection( + AuthenticatedConnection { + id: validated.id, + device_id: validated.device_id, + device_name: Some(device_name), + connected_at: now_ms, + }, + )), None => Ok(EngineEvent::Response(error_response( Some(&validated.id), "invalid_payload", @@ -1613,7 +1628,7 @@ mod tests { } #[test] - fn authenticated_ping_accepts_legacy_empty_payload_and_emits_valid_name_updates() { + fn authenticated_ping_emits_connection_events_for_legacy_and_named_payloads() { let mut engine = ProtocolEngine::new("desktop-1".into()); engine.set_paired_token("remote-1".into(), TOKEN.into()); @@ -1626,12 +1641,17 @@ mod tests { "payload": {} }); sign(&mut legacy, TOKEN, SlashEscaping::AndroidHtmlSafe); - let EngineEvent::Response(response) = + let EngineEvent::AuthenticatedConnection(legacy_connection) = engine.process_message(&legacy.to_string(), NOW).unwrap() else { - panic!("expected the legacy ping acknowledgement"); + panic!("expected an authenticated connection"); }; - assert!(response.contains("\"type\":\"ack\"")); + assert_eq!(legacy_connection.device_id, "remote-1"); + assert_eq!(legacy_connection.device_name, None); + assert_eq!(legacy_connection.connected_at, NOW); + assert!(engine + .complete_authenticated_connection(&legacy_connection, false) + .contains("\"type\":\"ack\"")); let mut named = json!({ "version": 1, @@ -1642,18 +1662,19 @@ mod tests { "payload": { "deviceName": " Kitchen Remote 📱 " } }); sign(&mut named, TOKEN, SlashEscaping::AndroidHtmlSafe); - let EngineEvent::RemoteNameUpdate(update) = + let EngineEvent::AuthenticatedConnection(connection) = engine.process_message(&named.to_string(), NOW + 1).unwrap() else { - panic!("expected a Remote name update"); + panic!("expected an authenticated connection"); }; - assert_eq!(update.device_id, "remote-1"); - assert_eq!(update.device_name, "Kitchen Remote 📱"); + assert_eq!(connection.device_id, "remote-1"); + assert_eq!(connection.device_name.as_deref(), Some("Kitchen Remote 📱")); + assert_eq!(connection.connected_at, NOW + 1); assert!(engine - .complete_remote_name_update(&update, true) + .complete_authenticated_connection(&connection, true) .contains("\"type\":\"ack\"")); assert!(engine - .complete_remote_name_update(&update, false) + .complete_authenticated_connection(&connection, false) .contains("name_update_failed")); } diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 568e8753..becc7c02 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -434,49 +434,49 @@ impl AppModel { fn persist_unlocked(&self) -> Result<(), String> { self.storage.save(&self.persisted_state(None, None, None)) } - pub fn apply_remote_name(&self, device_id: &str, device_name: &str) -> Result { + pub fn record_authenticated_connection( + &self, + device_id: &str, + device_name: Option<&str>, + connected_at: i64, + ) -> Result { let _transaction = self .persistence_lock .lock() .unwrap_or_else(|poisoned| poisoned.into_inner()); - let previous = { - let mut data = self - .shared - .lock() - .unwrap_or_else(|poisoned| poisoned.into_inner()); - let Some(device) = data - .state - .paired_devices - .iter_mut() - .find(|device| device.device_id == device_id) - else { - return Ok(false); - }; - let previous_name = std::mem::replace(&mut device.device_name, device_name.to_owned()); - let previous_connected_name = data.state.connected_device_name.clone(); - if previous_connected_name.is_some() { - data.state.connected_device_name = Some(device_name.to_owned()); - } - (previous_name, previous_connected_name) + let mut candidate = self.persisted_state(None, None, None); + let Some(candidate_device) = candidate + .paired_devices + .iter_mut() + .find(|device| device.device_id == device_id) + else { + return Ok(false); }; - if let Err(error) = self.persist_unlocked() { - let mut data = self - .shared - .lock() - .unwrap_or_else(|poisoned| poisoned.into_inner()); - if let Some(device) = data - .state - .paired_devices - .iter_mut() - .find(|device| device.device_id == device_id) - .filter(|device| device.device_name == device_name) - { - device.device_name = previous.0; - } - if data.state.connected_device_name.as_deref() == Some(device_name) { - data.state.connected_device_name = previous.1; + candidate_device.last_seen_at = Some(connected_at); + if let Some(device_name) = device_name { + candidate_device.device_name = device_name.to_owned(); + } + + self.storage.save(&candidate)?; + + let mut data = self + .shared + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()); + let Some(device) = data + .state + .paired_devices + .iter_mut() + .find(|device| device.device_id == device_id) + else { + return Ok(false); + }; + device.last_seen_at = Some(connected_at); + if let Some(device_name) = device_name { + device.device_name = device_name.to_owned(); + if data.state.connected_device_name.is_some() { + data.state.connected_device_name = Some(device_name.to_owned()); } - return Err(error); } Ok(true) } @@ -912,14 +912,16 @@ mod tests { } #[test] - fn remote_name_updates_persist_and_refresh_connected_state() { - let root = std::env::temp_dir().join(format!("switchify-remote-name-{}", Uuid::new_v4())); + fn authenticated_connections_persist_time_and_refresh_the_remote_name() { + let root = + std::env::temp_dir().join(format!("switchify-last-connection-{}", Uuid::new_v4())); let state_path = root.join("state.json"); - let model = AppModel::with_storage_for_test(AppStorage::at(state_path)); + let model = AppModel::with_storage_for_test(AppStorage::at(state_path.clone())); + let device_id = format!("remote-{}", Uuid::new_v4()); { let mut data = model.shared.lock().unwrap(); data.state.paired_devices.push(PairedDeviceView { - device_id: "remote-1".into(), + device_id: device_id.clone(), device_name: "Old name".into(), paired_at: 1, last_seen_at: None, @@ -929,26 +931,42 @@ mod tests { model.persist().unwrap(); assert!(model - .apply_remote_name("remote-1", "Kitchen Remote") + .record_authenticated_connection(&device_id, Some("Kitchen Remote"), 42) .unwrap()); let state = model.snapshot(); assert_eq!(state.paired_devices[0].device_name, "Kitchen Remote"); + assert_eq!(state.paired_devices[0].last_seen_at, Some(42)); assert_eq!( state.connected_device_name.as_deref(), Some("Kitchen Remote") ); - assert_eq!( - model.storage.load().unwrap().paired_devices[0].device_name, - "Kitchen Remote" - ); - assert!(!model.apply_remote_name("missing", "Unknown").unwrap()); + + assert!(model + .record_authenticated_connection(&device_id, None, 99) + .unwrap()); + let state = model.snapshot(); + assert_eq!(state.paired_devices[0].device_name, "Kitchen Remote"); + assert_eq!(state.paired_devices[0].last_seen_at, Some(99)); + assert!(!model + .record_authenticated_connection("missing", None, 100) + .unwrap()); + + model + .storage + .save_pairing_token(&device_id, "test-token") + .unwrap(); + let restored = AppModel::with_storage_for_test(AppStorage::at(state_path)); + let restored_device = &restored.snapshot().paired_devices[0]; + assert_eq!(restored_device.device_name, "Kitchen Remote"); + assert_eq!(restored_device.last_seen_at, Some(99)); + restored.storage.delete_pairing_token(&device_id).unwrap(); let _ = fs::remove_dir_all(root); } #[test] - fn remote_name_update_rolls_back_when_persistence_fails() { + fn authenticated_connection_stays_unchanged_when_persistence_fails() { let root = - std::env::temp_dir().join(format!("switchify-remote-name-fail-{}", Uuid::new_v4())); + std::env::temp_dir().join(format!("switchify-last-connection-fail-{}", Uuid::new_v4())); let state_path = root.join("state.json"); let model = AppModel::with_storage_for_test(AppStorage::at(state_path.clone())); { @@ -957,7 +975,7 @@ mod tests { device_id: "remote-1".into(), device_name: "Old name".into(), paired_at: 1, - last_seen_at: None, + last_seen_at: Some(7), }); data.state.connected_device_name = Some("Old name".into()); } @@ -965,9 +983,12 @@ mod tests { fs::remove_file(&state_path).unwrap(); fs::create_dir(&state_path).unwrap(); - assert!(model.apply_remote_name("remote-1", "New name").is_err()); + assert!(model + .record_authenticated_connection("remote-1", Some("New name"), 42) + .is_err()); let state = model.snapshot(); assert_eq!(state.paired_devices[0].device_name, "Old name"); + assert_eq!(state.paired_devices[0].last_seen_at, Some(7)); assert_eq!(state.connected_device_name.as_deref(), Some("Old name")); let _ = fs::remove_dir_all(root); } diff --git a/src-tauri/src/windows_runtime.rs b/src-tauri/src/windows_runtime.rs index e13ac6f2..f5b4e9c6 100644 --- a/src-tauri/src/windows_runtime.rs +++ b/src-tauri/src/windows_runtime.rs @@ -821,24 +821,28 @@ fn process_frame( replaced_response } Some(EngineEvent::Response(response)) => Some(response), - Some(EngineEvent::RemoteNameUpdate(update)) => { + Some(EngineEvent::AuthenticatedConnection(connection)) => { let response = { let model = app.state::(); let saved = model - .apply_remote_name(&update.device_id, &update.device_name) + .record_authenticated_connection( + &connection.device_id, + connection.device_name.as_deref(), + connection.connected_at, + ) .unwrap_or(false); if !saved { set_activity( shared, ActivityKind::Error, - "The Remote name could not be saved.", + "The device connection details could not be saved.", ); } shared .lock() .unwrap_or_else(|poisoned| poisoned.into_inner()) .engine - .complete_remote_name_update(&update, saved) + .complete_authenticated_connection(&connection, saved) }; Some(response) } diff --git a/src/App.test.tsx b/src/App.test.tsx index 01f84adb..1c6c3cde 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -48,6 +48,21 @@ describe("Switchify PC shell", () => { expect(within(footer as HTMLElement).queryByRole("button")).not.toBeInTheDocument(); }); + it("shows when paired devices last connected", async () => { + const connectedAt = Date.UTC(2026, 7, 22, 12, 30); + browserState.pairedDevices = [ + { deviceId: "phone-1", deviceName: "Pixel", pairedAt: 1, lastSeenAt: connectedAt }, + { deviceId: "phone-2", deviceName: "Tablet", pairedAt: 2, lastSeenAt: null }, + ]; + + render(); + await screen.findByRole("heading", { name: "Switchify PC" }); + fireEvent.click(screen.getByRole("button", { name: "Devices" })); + + expect(screen.getByText(`Last connected ${new Date(connectedAt).toLocaleString()}`)).toBeInTheDocument(); + expect(screen.getByText("Not connected yet")).toBeInTheDocument(); + }); + it("keeps internal activity out of Home and Support while retaining troubleshooting history", async () => { browserState.bluetooth = "advertising"; browserState.lastActivity = { kind: "error", message: "Internal runtime activity" }; diff --git a/src/App.tsx b/src/App.tsx index 50f6388a..6ad81209 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -72,7 +72,7 @@ function HomeView({ state, onDisconnect, onAccessibility, onSetup }: { state: Ap function DevicesView({ state, forget }: { state: AppState; forget: (id: string) => void }) { return

Paired devices

Android devices trusted by this computer

{state.pairedDevices.length === 0 ?

No paired devices

Pair from Switchify Android while this computer is advertising.

: -
{state.pairedDevices.map((device) =>

{device.deviceName}

{device.lastSeenAt ? `Last seen ${new Date(device.lastSeenAt).toLocaleString()}` : "Not connected yet"}

)}
} +
{state.pairedDevices.map((device) =>

{device.deviceName}

{device.lastSeenAt !== null ? `Last connected ${new Date(device.lastSeenAt).toLocaleString()}` : "Not connected yet"}

)}
}
; } From 3852b50a74dc9c36791d87db3577b9752dbcb73f Mon Sep 17 00:00:00 2001 From: Owen McGirr Date: Sat, 22 Aug 2026 11:22:39 +0100 Subject: [PATCH 2/2] fix: preserve newest connection metadata --- src-tauri/src/macos.rs | 1 + src-tauri/src/protocol.rs | 45 +++++++++++---------- src-tauri/src/state.rs | 67 ++++++++++++++++++++++++++++++-- src-tauri/src/windows_runtime.rs | 1 + 4 files changed, 90 insertions(+), 24 deletions(-) diff --git a/src-tauri/src/macos.rs b/src-tauri/src/macos.rs index 5e72d8dc..4e21daa4 100644 --- a/src-tauri/src/macos.rs +++ b/src-tauri/src/macos.rs @@ -726,6 +726,7 @@ impl MacRuntime { &connection.device_id, connection.device_name.as_deref(), connection.connected_at, + connection.received_order, ) .unwrap_or(false); if !saved { diff --git a/src-tauri/src/protocol.rs b/src-tauri/src/protocol.rs index ae339318..34e52c9d 100644 --- a/src-tauri/src/protocol.rs +++ b/src-tauri/src/protocol.rs @@ -339,6 +339,7 @@ pub struct AuthenticatedConnection { pub device_id: String, pub device_name: Option, pub connected_at: i64, + pub received_order: u64, } #[derive(Debug, Clone, PartialEq)] @@ -384,6 +385,7 @@ pub struct ProtocolEngine { pending_pairings: HashMap, tokens: HashMap, replay_cache: HashMap, + connection_order: u64, } impl ProtocolEngine { @@ -394,6 +396,7 @@ impl ProtocolEngine { pending_pairings: HashMap::new(), tokens: HashMap::new(), replay_cache: HashMap::new(), + connection_order: 0, } } @@ -615,31 +618,31 @@ impl ProtocolEngine { }; match command_type { - "connection.ping" => match validated.payload.get("deviceName") { - None => Ok(EngineEvent::AuthenticatedConnection( + "connection.ping" => { + let device_name = match validated.payload.get("deviceName") { + None => None, + Some(value) => match value.as_str().and_then(normalize_remote_name) { + Some(device_name) => Some(device_name), + None => { + return Ok(EngineEvent::Response(error_response( + Some(&validated.id), + "invalid_payload", + "Remote name is invalid.", + ))) + } + }, + }; + self.connection_order = self.connection_order.saturating_add(1); + Ok(EngineEvent::AuthenticatedConnection( AuthenticatedConnection { id: validated.id, device_id: validated.device_id, - device_name: None, + device_name, connected_at: now_ms, + received_order: self.connection_order, }, - )), - Some(value) => match value.as_str().and_then(normalize_remote_name) { - Some(device_name) => Ok(EngineEvent::AuthenticatedConnection( - AuthenticatedConnection { - id: validated.id, - device_id: validated.device_id, - device_name: Some(device_name), - connected_at: now_ms, - }, - )), - None => Ok(EngineEvent::Response(error_response( - Some(&validated.id), - "invalid_payload", - "Remote name is invalid.", - ))), - }, - }, + )) + } "pointer.profile" => Ok(EngineEvent::PointerProfile(validated.id)), "mouse.move" => { let Some(dx) = bounded_number(&validated.payload, "dx", MAX_POINTER_DELTA) else { @@ -1649,6 +1652,7 @@ mod tests { assert_eq!(legacy_connection.device_id, "remote-1"); assert_eq!(legacy_connection.device_name, None); assert_eq!(legacy_connection.connected_at, NOW); + assert_eq!(legacy_connection.received_order, 1); assert!(engine .complete_authenticated_connection(&legacy_connection, false) .contains("\"type\":\"ack\"")); @@ -1670,6 +1674,7 @@ mod tests { assert_eq!(connection.device_id, "remote-1"); assert_eq!(connection.device_name.as_deref(), Some("Kitchen Remote 📱")); assert_eq!(connection.connected_at, NOW + 1); + assert_eq!(connection.received_order, 2); assert!(engine .complete_authenticated_connection(&connection, true) .contains("\"type\":\"ack\"")); diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index becc7c02..10b1475d 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -312,6 +313,7 @@ pub struct AppModel { pub telemetry: TelemetryService, emission_lock: Mutex<()>, persistence_lock: Mutex<()>, + connection_orders: Mutex>, preserved_paired_devices: Vec, } @@ -405,6 +407,7 @@ impl AppModel { telemetry, emission_lock: Mutex::new(()), persistence_lock: Mutex::new(()), + connection_orders: Mutex::new(HashMap::new()), preserved_paired_devices, }; let _ = model.persist(); @@ -439,6 +442,7 @@ impl AppModel { device_id: &str, device_name: Option<&str>, connected_at: i64, + received_order: u64, ) -> Result { let _transaction = self .persistence_lock @@ -452,6 +456,15 @@ impl AppModel { else { return Ok(false); }; + if self + .connection_orders + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + .get(device_id) + .is_some_and(|latest_order| *latest_order > received_order) + { + return Ok(true); + } candidate_device.last_seen_at = Some(connected_at); if let Some(device_name) = device_name { candidate_device.device_name = device_name.to_owned(); @@ -478,6 +491,10 @@ impl AppModel { data.state.connected_device_name = Some(device_name.to_owned()); } } + self.connection_orders + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + .insert(device_id.to_owned(), received_order); Ok(true) } #[cfg_attr(not(target_os = "windows"), allow(dead_code))] @@ -931,7 +948,7 @@ mod tests { model.persist().unwrap(); assert!(model - .record_authenticated_connection(&device_id, Some("Kitchen Remote"), 42) + .record_authenticated_connection(&device_id, Some("Kitchen Remote"), 42, 1) .unwrap()); let state = model.snapshot(); assert_eq!(state.paired_devices[0].device_name, "Kitchen Remote"); @@ -942,13 +959,13 @@ mod tests { ); assert!(model - .record_authenticated_connection(&device_id, None, 99) + .record_authenticated_connection(&device_id, None, 99, 2) .unwrap()); let state = model.snapshot(); assert_eq!(state.paired_devices[0].device_name, "Kitchen Remote"); assert_eq!(state.paired_devices[0].last_seen_at, Some(99)); assert!(!model - .record_authenticated_connection("missing", None, 100) + .record_authenticated_connection("missing", None, 100, 3) .unwrap()); model @@ -984,7 +1001,7 @@ mod tests { fs::create_dir(&state_path).unwrap(); assert!(model - .record_authenticated_connection("remote-1", Some("New name"), 42) + .record_authenticated_connection("remote-1", Some("New name"), 42, 1) .is_err()); let state = model.snapshot(); assert_eq!(state.paired_devices[0].device_name, "Old name"); @@ -993,6 +1010,48 @@ mod tests { let _ = fs::remove_dir_all(root); } + #[test] + fn stale_authenticated_connection_cannot_replace_newer_metadata() { + let root = + std::env::temp_dir().join(format!("switchify-stale-connection-{}", Uuid::new_v4())); + let state_path = root.join("state.json"); + let model = Arc::new(AppModel::with_storage_for_test(AppStorage::at(state_path))); + { + let mut data = model.shared.lock().unwrap(); + data.state.paired_devices.push(PairedDeviceView { + device_id: "remote-1".into(), + device_name: "Original name".into(), + paired_at: 1, + last_seen_at: None, + }); + } + model.persist().unwrap(); + + let (parsed_tx, parsed_rx) = mpsc::channel(); + let (continue_tx, continue_rx) = mpsc::channel(); + let older_model = model.clone(); + let older = thread::spawn(move || { + parsed_tx.send(()).unwrap(); + continue_rx.recv().unwrap(); + older_model.record_authenticated_connection("remote-1", Some("Stale name"), 42, 1) + }); + + parsed_rx.recv().unwrap(); + assert!(model + .record_authenticated_connection("remote-1", Some("Current name"), 99, 2) + .unwrap()); + continue_tx.send(()).unwrap(); + assert!(older.join().unwrap().unwrap()); + + let state = model.snapshot(); + assert_eq!(state.paired_devices[0].device_name, "Current name"); + assert_eq!(state.paired_devices[0].last_seen_at, Some(99)); + let stored = model.storage.load().unwrap(); + assert_eq!(stored.paired_devices[0].device_name, "Current name"); + assert_eq!(stored.paired_devices[0].last_seen_at, Some(99)); + let _ = fs::remove_dir_all(root); + } + #[test] fn legacy_true_setting_becomes_explicit_opt_in_but_false_stays_undecided() { let root = std::env::temp_dir().join(format!("switchify-consent-{}", uuid::Uuid::new_v4())); diff --git a/src-tauri/src/windows_runtime.rs b/src-tauri/src/windows_runtime.rs index f5b4e9c6..91010404 100644 --- a/src-tauri/src/windows_runtime.rs +++ b/src-tauri/src/windows_runtime.rs @@ -829,6 +829,7 @@ fn process_frame( &connection.device_id, connection.device_name.as_deref(), connection.connected_at, + connection.received_order, ) .unwrap_or(false); if !saved {