chore: only refresh checkouts when related settings change
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
- Restructure the backend around domain types: git operations behind a `Repo` type, the grasp push pipeline behind `GraspPush`, nostr connectivity behind `NostrBackend`, and shared helpers consolidated into `utils`
|
- Restructure the backend around domain types: git operations behind a `Repo` type, the grasp push pipeline behind `GraspPush`, nostr connectivity behind `NostrBackend`, and shared helpers consolidated into `utils`
|
||||||
- Fetch the logged-in user's grasp list, code follows, followed repositories, contacts, profile metadata, mute list, and blossom servers via gossip at login instead of the bootstrap relays
|
- Fetch the logged-in user's grasp list, code follows, followed repositories, contacts, profile metadata, mute list, and blossom servers via gossip at login instead of the bootstrap relays
|
||||||
- Connect to grasp relays and load the inbox only after the user's grasp list event arrives
|
- Connect to grasp relays and load the inbox only after the user's grasp list event arrives
|
||||||
|
- Refresh the checkouts store only when the checkouts settings change
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use gpui::{App, AppContext, Context, Entity, Global, Subscription};
|
use gpui::{App, AppContext, Context, Entity, Global, Subscription};
|
||||||
use nostr::prelude::*;
|
use nostr::prelude::*;
|
||||||
use settings::{CheckoutRecord, SettingsStore};
|
use settings::{CheckoutRecord, CheckoutsSettings, SettingsStore};
|
||||||
use signed_core::{Announcement, RepoAddr};
|
use signed_core::{Announcement, RepoAddr};
|
||||||
use signed_git::Repo;
|
use signed_git::Repo;
|
||||||
use utils::same_repo_url;
|
use utils::same_repo_url;
|
||||||
@@ -17,13 +17,11 @@ use crate::refresh::{RefreshGate, RefreshRequest};
|
|||||||
use crate::repos::RepoListStore;
|
use crate::repos::RepoListStore;
|
||||||
|
|
||||||
const REFRESH_DEBOUNCE: Duration = Duration::from_millis(300);
|
const REFRESH_DEBOUNCE: Duration = Duration::from_millis(300);
|
||||||
|
const MAX_STATUS_CHECKOUTS: usize = 8;
|
||||||
const LOCAL_POLL: Duration = Duration::from_secs(2);
|
const LOCAL_POLL: Duration = Duration::from_secs(2);
|
||||||
const STATUS_POLL: Duration = Duration::from_secs(15);
|
const STATUS_POLL: Duration = Duration::from_secs(15);
|
||||||
const PUSH_POLL: Duration = Duration::from_secs(60);
|
const PUSH_POLL: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
const MAX_STATUS_CHECKOUTS: usize = 8;
|
|
||||||
|
|
||||||
struct GlobalCheckoutsStore(Entity<CheckoutsStore>);
|
struct GlobalCheckoutsStore(Entity<CheckoutsStore>);
|
||||||
|
|
||||||
impl Global for GlobalCheckoutsStore {}
|
impl Global for GlobalCheckoutsStore {}
|
||||||
@@ -31,13 +29,13 @@ impl Global for GlobalCheckoutsStore {}
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CheckoutStatus {
|
pub struct CheckoutStatus {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
// A detached checkout is idle and yields no status.
|
/// A detached checkout is idle and yields no status.
|
||||||
pub branch: String,
|
pub branch: String,
|
||||||
// For tip-based PR dedupe.
|
/// For tip-based PR dedupe.
|
||||||
pub head: String,
|
pub head: String,
|
||||||
// `refs/remotes/origin/<branch>`, else `origin/HEAD` for new branches.
|
/// `refs/remotes/origin/<branch>`, else `origin/HEAD` for new branches.
|
||||||
pub base: String,
|
pub base: String,
|
||||||
// Zero-ahead checkouts are dropped, so always above zero.
|
/// Zero-ahead checkouts are dropped, so always above zero.
|
||||||
pub ahead: u32,
|
pub ahead: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +55,8 @@ pub struct CheckoutsStore {
|
|||||||
refresh: RefreshGate,
|
refresh: RefreshGate,
|
||||||
debounce_pending: bool,
|
debounce_pending: bool,
|
||||||
local_pending: bool,
|
local_pending: bool,
|
||||||
// The local pass runs a full pass again once this is older than the
|
|
||||||
// reconciliation cadence, so remote moves still land.
|
|
||||||
last_full_sync: Option<Instant>,
|
last_full_sync: Option<Instant>,
|
||||||
|
checkouts_settings: CheckoutsSettings,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +71,7 @@ impl CheckoutsStore {
|
|||||||
|
|
||||||
pub fn new(cx: &mut Context<Self>) -> Self {
|
pub fn new(cx: &mut Context<Self>) -> Self {
|
||||||
let mut subscriptions = Vec::new();
|
let mut subscriptions = Vec::new();
|
||||||
|
let mut checkouts_settings = CheckoutsSettings::default();
|
||||||
|
|
||||||
if !cfg!(target_arch = "wasm32") {
|
if !cfg!(target_arch = "wasm32") {
|
||||||
let settings = SettingsStore::global(cx);
|
let settings = SettingsStore::global(cx);
|
||||||
@@ -81,7 +79,15 @@ impl CheckoutsStore {
|
|||||||
let repos = RepoListStore::global(cx);
|
let repos = RepoListStore::global(cx);
|
||||||
let backend = Backend::global(cx);
|
let backend = Backend::global(cx);
|
||||||
|
|
||||||
subscriptions.push(cx.observe(&settings, |this, _settings, cx| {
|
checkouts_settings = settings.read(cx).settings().checkouts.clone();
|
||||||
|
|
||||||
|
subscriptions.push(cx.observe(&settings, |this, settings, cx| {
|
||||||
|
let checkouts = settings.read(cx).settings().checkouts.clone();
|
||||||
|
if this.checkouts_settings == checkouts {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Only edits to the checkouts section affect the derived state
|
||||||
|
this.checkouts_settings = checkouts;
|
||||||
this.refresh(cx);
|
this.refresh(cx);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -128,6 +134,7 @@ impl CheckoutsStore {
|
|||||||
debounce_pending: false,
|
debounce_pending: false,
|
||||||
local_pending: false,
|
local_pending: false,
|
||||||
last_full_sync: None,
|
last_full_sync: None,
|
||||||
|
checkouts_settings,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,9 +193,6 @@ impl CheckoutsStore {
|
|||||||
self.refresh(cx);
|
self.refresh(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop the stale ready-to-push status and notify observers right away, so
|
|
||||||
// the sidebar badge updates immediately instead of waiting for the next
|
|
||||||
// background pass. The debounced refresh reconciles the remaining checkouts.
|
|
||||||
pub fn checkout_pushed(&mut self, addr: &RepoAddr, path: &Path, cx: &mut Context<Self>) {
|
pub fn checkout_pushed(&mut self, addr: &RepoAddr, path: &Path, cx: &mut Context<Self>) {
|
||||||
let mut removed = false;
|
let mut removed = false;
|
||||||
|
|
||||||
@@ -206,8 +210,6 @@ impl CheckoutsStore {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The other checkouts of this repository still need re-deriving
|
|
||||||
// against the remote, now that the pushed refs landed there.
|
|
||||||
self.request_push_statuses(addr, cx);
|
self.request_push_statuses(addr, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +233,9 @@ impl CheckoutsStore {
|
|||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
cx.background_executor().timer(REFRESH_DEBOUNCE).await;
|
cx.background_executor().timer(REFRESH_DEBOUNCE).await;
|
||||||
this.update(cx, |this, cx| this.run_refresh(cx))
|
this.update(cx, |this, cx| {
|
||||||
|
this.run_refresh(cx);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
@@ -287,14 +291,17 @@ impl CheckoutsStore {
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let origin = Repo::open(path)
|
let origin = Repo::open(path)
|
||||||
.and_then(|repo| repo.origin_url())
|
.and_then(|repo| repo.origin_url())
|
||||||
.ok()
|
.ok()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let root = Repo::open(path)
|
let root = Repo::open(path)
|
||||||
.and_then(|repo| repo.root_commit())
|
.and_then(|repo| repo.root_commit())
|
||||||
.ok()
|
.ok()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
facts.push((path.clone(), origin, root));
|
facts.push((path.clone(), origin, root));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +351,9 @@ impl CheckoutsStore {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
if again {
|
if again {
|
||||||
this.update(cx, |this, cx| this.refresh(cx))?;
|
this.update(cx, |this, cx| {
|
||||||
|
this.refresh(cx);
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
@@ -375,7 +384,6 @@ impl CheckoutsStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn local_tick(&mut self, cx: &mut Context<Self>) {
|
fn local_tick(&mut self, cx: &mut Context<Self>) {
|
||||||
// Nothing watched: the pass idles until a new request restarts it.
|
|
||||||
if self.status_requested.is_empty() && self.push_requested.is_empty() {
|
if self.status_requested.is_empty() && self.push_requested.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -433,8 +441,6 @@ impl CheckoutsStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
// The tracking refs move only when a full pass fetches; a full
|
|
||||||
// pass or a fresh request will apply fresher data.
|
|
||||||
if this.refresh.running() || this.debounce_pending {
|
if this.refresh.running() || this.debounce_pending {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,23 +20,19 @@ impl Global for GlobalRepoListStore {}
|
|||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
pub struct RepoActivityCounts {
|
pub struct RepoActivityCounts {
|
||||||
pub issues: u32,
|
pub issues: u32,
|
||||||
// PR updates are not new PRs and do not count.
|
|
||||||
pub pull_requests: u32,
|
pub pull_requests: u32,
|
||||||
pub commits: u32,
|
pub commits: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepoActivityCounts {
|
impl RepoActivityCounts {
|
||||||
// The popularity ranking key.
|
|
||||||
pub fn score(self) -> u32 {
|
pub fn score(self) -> u32 {
|
||||||
self.issues + self.pull_requests + self.commits
|
self.issues + self.pull_requests + self.commits
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepoListStore {
|
pub struct RepoListStore {
|
||||||
// Shared so views can clone the list per frame without a deep copy.
|
|
||||||
pub announcements: Arc<Vec<Announcement>>,
|
pub announcements: Arc<Vec<Announcement>>,
|
||||||
pub last_activity: Arc<HashMap<RepoAddr, Timestamp>>,
|
pub last_activity: Arc<HashMap<RepoAddr, Timestamp>>,
|
||||||
// For the Popular ranking of the explore list.
|
|
||||||
pub counts: Arc<HashMap<RepoAddr, RepoActivityCounts>>,
|
pub counts: Arc<HashMap<RepoAddr, RepoActivityCounts>>,
|
||||||
state_synced_repos: HashSet<RepoAddr>,
|
state_synced_repos: HashSet<RepoAddr>,
|
||||||
refresh: RefreshGate,
|
refresh: RefreshGate,
|
||||||
|
|||||||
Reference in New Issue
Block a user