feat(sync): round trip cloud snapshots

This commit is contained in:
2026-05-16 00:01:28 -04:00
parent 06e947f43e
commit 40ab6b4874
22 changed files with 694 additions and 69 deletions
+65 -8
View File
@@ -242,12 +242,15 @@ impl ElyShell {
}
}
/// Push the active profile's bookmarks to `ely-browser-cloud` as a
/// snapshot. The HTTP round-trip runs on a dedicated worker thread
/// (the UI thread never blocks on the network), and the worker
/// reports back through the shell's `sync_inbox` so the sync page
/// reflects the new state without waiting for a manual refresh.
pub(crate) fn trigger_cloud_sync_upload(&mut self) {
self.trigger_cloud_sync_upload_with_clock_floor(None);
}
pub(crate) fn trigger_cloud_sync_upload_after_remote(&mut self, logical_clock_floor: u64) {
self.trigger_cloud_sync_upload_with_clock_floor(Some(logical_clock_floor));
}
fn trigger_cloud_sync_upload_with_clock_floor(&mut self, logical_clock_floor: Option<u64>) {
let ShellState::Ready(core) = &self.state else {
return;
};
@@ -273,9 +276,13 @@ impl ElyShell {
}
};
let tx = self.sync_inbox_tx.clone();
let thread_name =
if logical_clock_floor.is_some() { "ely-sync-merge-upload" } else { "ely-sync-upload" };
std::thread::Builder::new()
.name("ely-sync-upload".to_string())
.spawn(move || run_sync_upload(profile_dir, device_name, bytes, tx))
.name(thread_name.to_string())
.spawn(move || {
run_sync_upload(profile_dir, device_name, bytes, logical_clock_floor, tx)
})
.map(|_| ())
.unwrap_or_else(|error| {
tracing::warn!(
@@ -317,6 +324,7 @@ fn run_sync_upload(
profile_dir: std::path::PathBuf,
device_name: String,
bytes: Vec<u8>,
logical_clock_floor: Option<u64>,
inbox: std::sync::mpsc::Sender<SyncStateUpdate>,
) {
let mut engine = match SyncEngine::for_profile_dir(
@@ -332,11 +340,60 @@ fn run_sync_upload(
return;
}
};
match engine.upload_bytes(bytes) {
let outcome = match logical_clock_floor {
Some(floor) => engine.upload_merged_bytes(bytes, floor),
None => engine.sync_bytes(bytes),
};
match outcome {
Ok(ely_browser_core::SyncOutcome::SignedOut) => {
tracing::info!(target: "ely::sync", "no bearer token on disk; sync skipped");
let _ = inbox.send(SyncStateUpdate::SignedOut);
}
Ok(ely_browser_core::SyncOutcome::AwaitingDeviceApproval { device_id }) => {
tracing::info!(
target: "ely::sync",
device_id = %device_id,
"sync device is awaiting approval",
);
let _ = inbox.send(SyncStateUpdate::AwaitingDeviceApproval);
}
Ok(ely_browser_core::SyncOutcome::RemoteSnapshot {
snapshot_id,
logical_clock,
payload_bytes,
device_id,
bytes,
}) => {
tracing::info!(
target: "ely::sync",
snapshot_id = %snapshot_id,
logical_clock,
payload_bytes,
device_id = %device_id,
"remote snapshot downloaded",
);
let _ = inbox.send(SyncStateUpdate::RemoteSnapshot { bytes, logical_clock });
}
Ok(ely_browser_core::SyncOutcome::AlreadyCurrent {
snapshot_id,
logical_clock,
payload_bytes,
device_id,
}) => {
tracing::info!(
target: "ely::sync",
snapshot_id = %snapshot_id,
logical_clock,
payload_bytes,
device_id = %device_id,
"snapshot already current",
);
let last_synced_at_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let _ = inbox.send(SyncStateUpdate::SyncReady { last_synced_at_secs });
}
Ok(ely_browser_core::SyncOutcome::Uploaded {
snapshot_id,
logical_clock,