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:
@@ -1,6 +1,22 @@
|
||||
/// Connection lifecycle of the cloud sync client.
|
||||
///
|
||||
/// The previous variant set was a single `SignedOut`, which made the
|
||||
/// Sync settings page report "Local-only · sign-in coming soon" even
|
||||
/// after the user dropped a bearer token in. The state machine now
|
||||
/// progresses from `SignedOut` → `SignedIn` (token present, sync not
|
||||
/// yet attempted) → `SyncReady` (last upload landed) or `SyncError`
|
||||
/// (last upload failed). `AwaitingDeviceApproval` is the dedicated
|
||||
/// state for the 403 the worker returns when the device-id bound to
|
||||
/// the session has not yet been approved by another already-approved
|
||||
/// device — surfacing it as its own variant lets the UI explain the
|
||||
/// gap instead of bucketing it into a generic error.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SyncConnectionState {
|
||||
SignedOut,
|
||||
SignedIn,
|
||||
AwaitingDeviceApproval,
|
||||
SyncReady { last_synced_at_secs: u64 },
|
||||
SyncError { message: String },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -28,6 +44,7 @@ pub enum SyncObjectState {
|
||||
LocalOnly,
|
||||
Paused,
|
||||
PrivacyControlled,
|
||||
Synced,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -84,14 +101,21 @@ impl SyncObjectStatus {
|
||||
}
|
||||
|
||||
impl SyncStatus {
|
||||
#[must_use]
|
||||
pub fn new(connection: SyncConnectionState, objects: Vec<SyncObjectStatus>) -> Self {
|
||||
let failed_objects = match &connection {
|
||||
SyncConnectionState::SyncError { .. } => 1,
|
||||
_ => 0,
|
||||
};
|
||||
Self { connection, pending_objects: 0, failed_objects, objects }
|
||||
}
|
||||
|
||||
/// Convenience constructor preserved for tests + call sites that
|
||||
/// have not been migrated to [`SyncStatus::new`] yet. Same result
|
||||
/// as `SyncStatus::new(SyncConnectionState::SignedOut, objects)`.
|
||||
#[must_use]
|
||||
pub fn signed_out(objects: Vec<SyncObjectStatus>) -> Self {
|
||||
Self {
|
||||
connection: SyncConnectionState::SignedOut,
|
||||
pending_objects: 0,
|
||||
failed_objects: 0,
|
||||
objects,
|
||||
}
|
||||
Self::new(SyncConnectionState::SignedOut, objects)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
Reference in New Issue
Block a user