Reshape SyncStatusStore into a thin probe-and-emit store

This commit is contained in:
2026-10-15 18:51:10 +07:00
parent 7be3935515
commit 2cd1068351
4 changed files with 154 additions and 141 deletions
+23 -16
View File
@@ -79,11 +79,13 @@ Two pieces replace the fat global:
directories cannot cheaply recompute git statuses, so re-querying per
directory would multiply the expensive part.
- Subscribes to `Backend` (`RepoUpdates`/`Synced` → probe active;
`SignerChanged` → clear active + in-flight, emit empty so directories
clear) and observes `CheckoutsStore` (associations changed → probe
active). The `RepoListStore` observation edge disappears: targets now
come from directories, and announcement refreshes re-track through the
sidebar's own data path.
`SignerChanged` → clear active, emit empty so directories clear) and
observes `CheckoutsStore` (associations changed → probe active). The
`RepoListStore` observation edge disappears: targets now come from
directories, and announcement refreshes re-track through the sidebar's
own data path.
- Probe requests reach the loop over the same flume-channel wake the
profile fetch service uses, so `probe` needs only `&self`.
- Polls `active` every 180s while it is non-empty; no user signed in →
clear and idle, same as today.
2. `SyncStatusDirectory` (per surface entity): the statuses one surface
@@ -121,7 +123,7 @@ mechanism the profile directories rely on, and it composes with
```rust
// signed_state/src/sync_status.rs
/// Fresh statuses for these repositories; empty on signer change.
/// Statuses for these repositories may have changed; empty on signer change.
#[derive(Debug, Clone)]
pub enum SyncEvent {
Updated(Vec<(RepoAddr, Vec<CheckoutSyncStatus>)>),
@@ -132,8 +134,9 @@ impl EventEmitter<SyncEvent> for SyncStatusStore {}
/// Thin global: probes tracked repos and broadcasts results. Holds no data.
pub struct SyncStatusStore {
active: RefCell<HashSet<RepoAddr>>,
in_flight: RefCell<bool>,
pass_pending: RefCell<bool>,
sender: Sender<()>,
in_flight: bool,
pass_pending: bool,
poll_pending: bool,
tasks: Vec<Task<()>>,
_subscriptions: Vec<Subscription>,
@@ -198,12 +201,13 @@ these two views.
## Migration steps
1. `signed_state`: reshape `sync_status.rs` — add `SyncEvent` +
`EventEmitter`, move the probe computation into a service method that
emits results, and strip the store of `statuses`, `statuses_of`,
`unsynced`, and `RefreshGate` (still used by `RepoListStore` and
`CheckoutsStore`, so `refresh.rs` stays). The `Backend` subscription and
the 180s poll move to the service; the `RepoListStore` observation is
dropped.
`EventEmitter`, move the probe computation into a pass that emits results,
and strip the store of `statuses`, `statuses_of`, `unsynced`, and
`RefreshGate` (still used by `RepoListStore` and `CheckoutsStore`, so
`refresh.rs` stays). The `Backend` subscription and the 180s poll move to
the service; the `RepoListStore` observation is dropped. `RepoStore::run_push`
replaces its post-push `refresh(cx)` with `probe(addr)` so badges update
without waiting for the relay pump.
2. `signed_state`: add `SyncStatusDirectory` in the same file and export it
from `lib.rs`. `init` keeps creating the global store.
3. `workspace`: rewire `SidebarPanel` (directory + `track` in `refresh`,
@@ -221,8 +225,11 @@ these two views.
- **Checkout association added mid-pass**: `CheckoutsStore` observation
schedules a follow-up pass, so the new path is picked up promptly, not on
the next 180s tick.
- **Signer change mid-pass**: directories clear on the empty `Updated` event;
a late result for an untracked addr is dropped by `apply`.
- **Signer change mid-pass**: the in-flight pass is left alone so its
completion releases the slot exactly once; its stale results are dropped by
directories because the addrs are no longer tracked. The empty `Updated`
event clears directories; a later probe re-runs over the new signer's
addrs.
- **No published state for a repo**: the pass skips it (`states.get` miss),
statuses stay empty, and the pending set is released like today's
missing-entry behavior.