Reflect real sync state on the Sync settings page
`SyncConnectionState` was a one-variant enum (`SignedOut`), so the
Sync page rendered "Local-only · sign-in coming soon" even after the
bearer token landed on disk and the upload thread completed. The
state machine now mirrors the actual lifecycle.
What lands:
- `SyncConnectionState` gains `SignedIn`, `AwaitingDeviceApproval`,
`SyncReady { last_synced_at_secs }`, `SyncError { message }`.
`SyncObjectState::Synced` joins the per-object enum so individual
rows can advertise "Synced" once a successful upload lands.
- `BrowserCore` stores the current `SyncConnectionState` and exposes
`set_sync_connection_state`. `sync_status` now propagates the live
state into the snapshot the UI reads.
- `ElyShell::probe_initial_sync_state` inspects
`<profile_data>/sync/bearer.token` synchronously at construction
so the first render of the sync page is honest about whether the
user is signed in.
- A `std::sync::mpsc` channel ferries upload outcomes from the
off-thread worker back to the shell; the existing 8 ms tick
drains it and stamps `core.set_sync_connection_state` with the
freshest result. The UI now shows "Signed in · awaiting first
sync", "Synced · last upload Xm ago", "Sync error · …", and the
worker-special "Signed in · waiting for device approval" when the
server returns `device_not_approved`.
This commit is contained in:
@@ -243,10 +243,9 @@ 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 result is
|
||||
/// emitted via `tracing` so the user can inspect it through
|
||||
/// `RUST_LOG=ely::sync=info`. No bearer token on disk → the
|
||||
/// engine reports `SignedOut` and the click is a no-op.
|
||||
/// (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(super) fn trigger_cloud_sync_upload(&mut self, _cx: &mut Context<Self>) {
|
||||
let ShellState::Ready(core) = &self.state else {
|
||||
return;
|
||||
@@ -272,9 +271,10 @@ impl ElyShell {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let tx = self.sync_inbox_tx.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("ely-sync-upload".to_string())
|
||||
.spawn(move || run_sync_upload(profile_dir, device_name, bytes))
|
||||
.spawn(move || run_sync_upload(profile_dir, device_name, bytes, tx))
|
||||
.map(|_| ())
|
||||
.unwrap_or_else(|error| {
|
||||
tracing::warn!(
|
||||
@@ -312,29 +312,56 @@ impl ElyShell {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_sync_upload(profile_dir: std::path::PathBuf, device_name: String, bytes: Vec<u8>) {
|
||||
fn run_sync_upload(
|
||||
profile_dir: std::path::PathBuf,
|
||||
device_name: String,
|
||||
bytes: Vec<u8>,
|
||||
inbox: std::sync::mpsc::Sender<super::SyncStateUpdate>,
|
||||
) {
|
||||
let mut engine = match SyncEngine::for_profile_dir(&profile_dir, device_name, sync_platform()) {
|
||||
Ok(engine) => engine,
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
target: "ely::sync",
|
||||
error = %error,
|
||||
"could not initialise sync engine",
|
||||
);
|
||||
let message = error.to_string();
|
||||
tracing::warn!(target: "ely::sync", error = %message, "could not initialise sync engine");
|
||||
let _ = inbox.send(super::SyncStateUpdate::SyncError { message });
|
||||
return;
|
||||
}
|
||||
};
|
||||
match engine.upload_bytes(bytes) {
|
||||
Ok(outcome) => tracing::info!(
|
||||
target: "ely::sync",
|
||||
outcome = ?outcome,
|
||||
"snapshot upload complete",
|
||||
),
|
||||
Err(error) => tracing::warn!(
|
||||
target: "ely::sync",
|
||||
error = %error,
|
||||
"snapshot upload failed",
|
||||
),
|
||||
Ok(ely_browser_core::SyncOutcome::SignedOut) => {
|
||||
tracing::info!(target: "ely::sync", "no bearer token on disk; sync skipped");
|
||||
let _ = inbox.send(super::SyncStateUpdate::SignedOut);
|
||||
}
|
||||
Ok(ely_browser_core::SyncOutcome::Uploaded {
|
||||
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 upload complete",
|
||||
);
|
||||
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(super::SyncStateUpdate::SyncReady { last_synced_at_secs });
|
||||
}
|
||||
Err(error) => {
|
||||
let message = error.to_string();
|
||||
tracing::warn!(target: "ely::sync", error = %message, "snapshot upload failed");
|
||||
let update = if message.contains("device_not_approved") {
|
||||
super::SyncStateUpdate::AwaitingDeviceApproval
|
||||
} else {
|
||||
super::SyncStateUpdate::SyncError { message }
|
||||
};
|
||||
let _ = inbox.send(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user