This commit is contained in:
2026-09-03 15:22:37 +07:00
parent c0f06d095e
commit 018395d0c5
6 changed files with 767 additions and 42 deletions
+50 -6
View File
@@ -15,9 +15,11 @@ use gpui_component::button::{Button, ButtonVariants};
use gpui_component::input::InputState;
use gpui_component::{ActiveTheme, Icon, IconName, Sizable, StyledExt, h_flex, v_flex};
use signed_core::{Announcement, identifier_from_name};
use signed_state::{Backend, BackendEvent, LocalReposStore, Profile, ProfileStore, RepoListStore};
use signed_state::{
Backend, BackendEvent, CheckoutsStore, LocalReposStore, Profile, ProfileStore, RepoListStore,
};
use signed_ui::image_cache::{MAX_IMAGES, image_cache};
use signed_ui::{NavItem, PixelAvatar, UserAvatar, title_bar_drag_handlers};
use signed_ui::{CountBadge, NavItem, PixelAvatar, UserAvatar, title_bar_drag_handlers};
use super::{RepoDetailView, RepoListView, open_repo_panel};
@@ -47,6 +49,9 @@ pub struct SidebarPanel {
banner: SharedString,
/// Observes the local-repository scan so new discoveries re-render.
_local_repos_subscription: Subscription,
/// Observes the checkouts store, whose ready-to-push statuses feed the
/// badges on the user's repository rows.
_checkouts_subscription: Subscription,
_subscription: Subscription,
}
@@ -77,6 +82,11 @@ impl SidebarPanel {
cx.notify();
});
let checkouts_store = CheckoutsStore::global(cx);
let checkouts_subscription = cx.observe(&checkouts_store, |_, _, cx| {
cx.notify();
});
let mut panel = Self {
focus_handle: cx.focus_handle(),
dock_area,
@@ -86,6 +96,7 @@ impl SidebarPanel {
my_repos_subscription: None,
banner: pick_banner(),
_local_repos_subscription: local_repos_subscription,
_checkouts_subscription: checkouts_subscription,
_subscription: subscription,
};
@@ -96,7 +107,8 @@ impl SidebarPanel {
panel
}
/// (Re)create the store listing the current user's repositories.
/// (Re)create the store listing the current user's repositories,
/// and watch each of them for unpushed local work.
fn refresh_my_repos(&mut self, cx: &mut Context<Self>) {
self.my_repos_subscription = None;
@@ -105,7 +117,24 @@ impl SidebarPanel {
self.my_repos = author.map(|author| cx.new(|cx| RepoListStore::new(Some(author), cx)));
if let Some(store) = self.my_repos.as_ref() {
self.my_repos_subscription = Some(cx.observe(store, |_, _, cx| cx.notify()));
self.my_repos_subscription = Some(cx.observe(store, |_this, store, cx| {
cx.notify();
// These are the signed-in user's own repositories; request
// their ready-to-push statuses (deduplicated per repo) so
// the rows carry a badge while local work is unpushed.
let addrs: Vec<_> = store
.read(cx)
.announcements
.iter()
.map(|a| a.addr())
.collect();
let checkouts = CheckoutsStore::global(cx);
checkouts.update(cx, |checkouts, cx| {
for addr in addrs {
checkouts.request_push_statuses(&addr, cx);
}
});
}));
}
}
@@ -328,9 +357,24 @@ impl SidebarPanel {
.clone()
.unwrap_or_else(|| SharedString::from(announcement.id.clone()));
let avatar = PixelAvatar::new(format!("{}:{}", announcement.owner, announcement.id));
let announcement = announcement.clone();
NavItem::new(format!("my-repo:{}", announcement.id), name, avatar).on_click(
// A small badge with the unpushed commit count of the repository's
// local checkouts (ready to push to the grasp servers).
let unpushed: usize = CheckoutsStore::global(cx)
.read(cx)
.push_statuses_of(&announcement.addr())
.iter()
.map(|status| status.ahead as usize)
.sum();
let announcement = announcement.clone();
let mut row = NavItem::new(format!("my-repo:{}", announcement.id), name, avatar);
if unpushed > 0 {
row = row.suffix(CountBadge::new(unpushed));
}
row.on_click(
cx.listener(move |this, _ev, window, cx| this.open_repo(&announcement, window, cx)),
)
}