From 729e15e27ec17d364124e87137ead564ed27bdef Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Sun, 30 Aug 2026 09:18:38 +0700 Subject: [PATCH] update repo list --- crates/signed_state/src/lib.rs | 10 +++++++ crates/signed_state/src/repo_list.rs | 36 +++++++++++++++++++++++-- crates/workspace/src/views/repo_list.rs | 14 +++++++--- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/crates/signed_state/src/lib.rs b/crates/signed_state/src/lib.rs index 4d1ec70..ae3690d 100644 --- a/crates/signed_state/src/lib.rs +++ b/crates/signed_state/src/lib.rs @@ -37,6 +37,11 @@ pub fn init(db_path: impl AsRef, cx: &mut App) -> Entity { ProfileStore::set_global(cx.new(ProfileStore::new), cx); + // Start the explore list from the local database before the first + // window opens; relay syncs continue in the background, so the list + // never waits for them. + RepoListStore::set_global(cx.new(|cx| RepoListStore::new(None, cx)), cx); + // The clone cache is only meaningful on native platforms; the wasm // build registers an empty store so `GitStore::global` still works. GitStore::set_global(PathBuf::new(), cx); @@ -54,6 +59,11 @@ pub fn init(cx: &mut App) -> Entity { ProfileStore::set_global(cx.new(ProfileStore::new), cx); + // Start the explore list from the local database before the first + // window opens; relay syncs continue in the background, so the list + // never waits for them. + RepoListStore::set_global(cx.new(|cx| RepoListStore::new(None, cx)), cx); + GitStore::set_global(PathBuf::new(), cx); entity diff --git a/crates/signed_state/src/repo_list.rs b/crates/signed_state/src/repo_list.rs index 5c93224..66cfedd 100644 --- a/crates/signed_state/src/repo_list.rs +++ b/crates/signed_state/src/repo_list.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use std::time::Duration; use anyhow::Error; -use gpui::{AppContext, Context, Subscription, Task}; +use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task}; use nostr_sdk::prelude::*; use signed_core::{Announcement, Deletions, RepoAddr, filters, repo_addr}; @@ -16,7 +16,15 @@ const REFRESH_DEBOUNCE: Duration = Duration::from_millis(300); /// How far back activity events count toward a repository's last activity. const ACTIVITY_WINDOW: Duration = Duration::from_secs(90 * 86_400); +struct GlobalRepoListStore(Entity); + +impl Global for GlobalRepoListStore {} + /// Store listing repository announcements (global discovery or per-author). +/// +/// The all-repos store (`author: None`) is created at startup by +/// [`crate::init`] and installed as a global, so the explore panel renders +/// what's in the local database without waiting for relays. pub struct RepoListStore { /// Shared so views can clone the list per frame without a deep copy. pub announcements: Arc>, @@ -33,6 +41,16 @@ pub struct RepoListStore { } impl RepoListStore { + /// Retrieve the global explore store (all announcements, created at + /// startup by [`crate::init`]). + pub fn global(cx: &App) -> Entity { + cx.global::().0.clone() + } + + pub(crate) fn set_global(entity: Entity, cx: &mut App) { + cx.set_global(GlobalRepoListStore(entity)); + } + /// Create a store. If `author` is `None`, all announcements are listed. pub fn new(author: Option, cx: &mut Context) -> Self { let backend = Backend::global(cx); @@ -79,7 +97,9 @@ impl RepoListStore { }; store.subscribe_remote(cx); - store.refresh(cx); + // Query the local database right away; the list never waits for the + // relay syncs started above to finish. + store.refresh_initial(cx); store } @@ -107,6 +127,18 @@ impl RepoListStore { }); } + /// One-shot initial load: query the local database immediately (no + /// debounce), so stored announcements appear as soon as the app opens. + /// Only called from [`Self::new`], before any refresh can be pending. + fn refresh_initial(&mut self, cx: &mut Context) { + debug_assert!(!self.debouncing); + if self.refreshing { + self.refresh_dirty = true; + return; + } + self.run_refresh(cx); + } + /// Re-query the local database. Latest announcement per repository wins. /// /// Debounced: a short delay collapses bursts of requests (e.g. sync diff --git a/crates/workspace/src/views/repo_list.rs b/crates/workspace/src/views/repo_list.rs index 9e4d5f5..2518034 100644 --- a/crates/workspace/src/views/repo_list.rs +++ b/crates/workspace/src/views/repo_list.rs @@ -37,7 +37,10 @@ impl RepoListView { _window: &mut Window, cx: &mut Context, ) -> Self { - let store = cx.new(|cx| RepoListStore::new(None, cx)); + // Created at startup by `signed_state::init`: the local database has + // already been queried, so stored repositories appear immediately + // without waiting for relays (which only add to the list). + let store = RepoListStore::global(cx); let subscription = cx.observe(&store, |this, store, cx| { // Each virtual list row holds `COLUMNS` repo cards. @@ -48,12 +51,17 @@ impl RepoListView { } }); + // The store may already hold announcements (it loaded before the + // panel opened), so seed the row sizes right away. + let rows = store.read(cx).announcements.len().div_ceil(COLUMNS); + let item_sizes = Rc::new(vec![size(px(0.), px(CARD_HEIGHT)); rows]); + Self { store, dock_area, focus_handle: cx.focus_handle(), scroll_handle: VirtualListScrollHandle::new(), - item_sizes: Rc::new(vec![]), + item_sizes, _subscription: subscription, } } @@ -230,7 +238,7 @@ impl Render for RepoListView { div() .text_sm() .text_color(cx.theme().muted_foreground) - .child("No repositories found. Waiting for relays..."), + .child("No repositories found yet."), ), ) })