chore: clean up codebase (#19)
Rust / build (macos-latest, stable) (push) Canceled after 0s
Rust / build (ubuntu-latest, stable) (push) Canceled after 0s
Rust / build (windows-latest, stable) (push) Canceled after 0s

Reviewed-on: #19
This commit was merged in pull request #19.
This commit is contained in:
2026-09-13 09:42:08 +00:00
parent 40deb9db66
commit f6b8a5e133
82 changed files with 3559 additions and 7862 deletions
@@ -17,10 +17,9 @@ use super::super::open_repo_panel;
use super::grasp_servers::{GraspServersState, grasp_servers_field, load_user_grasp_servers};
use crate::views::dialog_state::{DialogProgress, error_row};
/// Shared state for the Create Repository dialog, so async results can be rendered.
/// Progress of the create-repository flow, so async results can be rendered.
pub type CreateRepoState = DialogProgress;
/// Open the Create Repository dialog.
pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) {
let settings = SettingsStore::global(cx);
let default_folder = settings
@@ -151,7 +150,6 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
});
}
/// Pick the repository's storage folder with the platform's native folder picker.
fn choose_folder(folder_input: &Entity<InputState>, window: &mut Window, cx: &mut App) {
let handle = window.window_handle();
let folder_input = folder_input.clone();
@@ -186,8 +184,6 @@ fn choose_folder(folder_input: &Entity<InputState>, window: &mut Window, cx: &mu
.detach();
}
/// Run the create-repository flow.
///
/// Opens the new working copy and the repository panel on success.
#[allow(clippy::too_many_arguments)]
fn create_repository(
@@ -250,12 +246,17 @@ fn create_repository(
.detach();
}
/// Open the newly created repository in the dock's center.
fn open_repo(
dock_area: WeakEntity<DockArea>,
announcement: Announcement,
window: &mut Window,
cx: &mut App,
) {
open_repo_panel(&dock_area, &announcement, window, cx);
open_repo_panel(
&dock_area,
&announcement.addr(),
Some(&announcement),
window,
cx,
);
}
@@ -11,17 +11,16 @@ use signed_state::Backend;
/// State of the grasp-server section of a publish dialog, so async results can be rendered.
#[derive(Default)]
pub struct GraspServersState {
/// The user's grasp list of kind `10317` is being loaded.
/// Set while the user's kind `10317` grasp list loads.
pub loading_servers: bool,
pub grasp_servers: Vec<RelayUrl>,
/// Whether the grasp server section is shown. Defaults to shown.
pub servers_enabled: bool,
/// Error of the last grasp-server edit, an invalid relay URL for example.
/// Error from the last grasp-server edit, such as an invalid relay URL.
pub error: Option<SharedString>,
}
impl GraspServersState {
/// Defaults used until the user's grasp list loads, which replaces them when non-empty.
/// Defaults used until the user's grasp list loads and replaces them.
///
/// Persisted settings supply the defaults, an empty list falls back to the built-ins.
pub fn new_default(settings: &GraspServersSettings) -> Self {
@@ -137,7 +136,6 @@ pub fn grasp_servers_field(
}))
}
/// One grasp server row, the host in a tag plus a remove button.
fn render_server_row(
ix: usize,
relay: &RelayUrl,
@@ -176,7 +174,7 @@ fn render_server_row(
)
}
/// The bare host of a grasp server, defaults are entered without a scheme.
/// Shows only the host, since grasp servers are entered without a scheme.
fn display_server(relay: &RelayUrl) -> SharedString {
relay
.domain()
@@ -184,7 +182,7 @@ fn display_server(relay: &RelayUrl) -> SharedString {
.unwrap_or_else(|| SharedString::from(relay.to_string()))
}
/// Parse the relay input, accepting a bare host, and append it to the list.
/// Accepts a bare host as well as a full URL.
fn add_relay(
state: &Entity<GraspServersState>,
input: &Entity<InputState>,
@@ -220,9 +218,9 @@ fn add_relay(
}
}
/// Load the user's grasp list of kind `10317` from the local database.
/// Loads the user's kind `10317` grasp list from the local database.
///
/// It replaces the defaults when it lists any servers.
/// Replaces the defaults when the list is non-empty.
pub fn load_user_grasp_servers(
state: Entity<GraspServersState>,
window: &mut Window,
@@ -1,7 +1,6 @@
use gpui::{App, Window, px};
use gpui_component::WindowExt;
/// Open the Import Identity dialog.
pub fn open(window: &mut Window, cx: &mut App) {
window.open_dialog(cx, move |dialog, _window, _cx| {
dialog.title("Import identity").width(px(400.))
+28 -36
View File
@@ -39,15 +39,13 @@ pub struct SidebarPanel {
dock_area: WeakEntity<DockArea>,
inbox: Option<WeakEntity<InboxView>>,
explore: Option<WeakEntity<RepoListView>>,
/// Artwork for the sign-in screen.
banner: SharedString,
/// The signed-in user's announced repositories, newest first.
announcements: Arc<Vec<Announcement>>,
/// Local repositories found by the scan that are not announced yet.
local_repos: Arc<Vec<PathBuf>>,
/// A local scan is currently running.
scanning: bool,
/// Unpushed local commits per announced repository, the row badge counts.
/// Unpushed commit counts per announced repository, shown as row badges.
unpushed: HashMap<RepoAddr, usize>,
_subscriptions: Vec<Subscription>,
}
@@ -80,21 +78,19 @@ impl SidebarPanel {
}
}));
// The merged list re-derives when announcements or the local scan change.
subscriptions.push(cx.observe(&repos, |this, _repos, cx| {
if this.refresh(cx) {
cx.notify();
}
}));
// The local scan re-derives when announcements or the local scan change.
subscriptions.push(cx.observe(&local, |this, _local, cx| {
if this.refresh(cx) {
cx.notify();
}
}));
// Push statuses are recomputed in the background; only the badge counts change.
// Push statuses are recomputed in the background, so only the badge counts change.
subscriptions.push(cx.observe(&checkouts, |this, _checkouts, cx| {
if this.refresh_unpushed(cx) {
cx.notify();
@@ -125,8 +121,7 @@ impl SidebarPanel {
.map(|user| repo_list.read(cx).announcements_of(user))
.unwrap_or_default();
// A scanned repository is dropped from the local list
// once the user announces it, so it is not listed twice.
// Drop a scanned repository once the user announces it, so it is not listed twice.
let local = LocalReposStore::global(cx);
let scanning = local.read(cx).scanning;
@@ -162,14 +157,13 @@ impl SidebarPanel {
announcements_changed || local_changed || scanning_changed
}
/// Recompute the badge counts from the global checkouts store's ready-to-push statuses
fn refresh_unpushed(&mut self, cx: &mut Context<Self>) -> bool {
let checkouts = CheckoutsStore::global(cx).read(cx);
let checkouts = CheckoutsStore::global(cx);
let mut unpushed = HashMap::with_capacity(self.announcements.len());
for announcement in self.announcements.iter() {
let addr = announcement.addr();
let count = checkouts.unpushed(&addr);
let count = checkouts.read(cx).unpushed(&addr);
if count > 0 {
unpushed.insert(addr, count);
}
@@ -183,7 +177,6 @@ impl SidebarPanel {
true
}
/// Keep the `ready to push` statuses of the announced repositories current.
fn request_push_watches(&self, cx: &mut Context<Self>) {
let checkouts = CheckoutsStore::global(cx);
checkouts.update(cx, |checkouts, cx| {
@@ -193,7 +186,6 @@ impl SidebarPanel {
});
}
/// Open the inbox home panel in the dock area's center.
pub fn open_inbox(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.inbox.as_ref().and_then(WeakEntity::upgrade).is_some() {
return;
@@ -202,12 +194,13 @@ impl SidebarPanel {
let panel = cx.new(|cx| InboxView::new(self.dock_area.clone(), cx));
self.inbox = Some(panel.downgrade());
let _ = self.dock_area.update(cx, |dock_area, cx| {
add_center_panel(dock_area, panel_handle(panel), window, cx);
});
self.dock_area
.update(cx, |dock_area, cx| {
add_center_panel(dock_area, panel_handle(panel), window, cx);
})
.ok();
}
/// Open the Explore repository list panel in the dock area's center.
pub fn open_explore(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self
.explore
@@ -221,12 +214,13 @@ impl SidebarPanel {
let panel = cx.new(|cx| RepoListView::new(self.dock_area.clone(), window, cx));
self.explore = Some(panel.downgrade());
let _ = self.dock_area.update(cx, |dock_area, cx| {
add_center_panel(dock_area, panel_handle(panel), window, cx);
});
self.dock_area
.update(cx, |dock_area, cx| {
add_center_panel(dock_area, panel_handle(panel), window, cx);
})
.ok();
}
/// Show the Onboarding dialog.
fn open_onboarding(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let name_input = cx.new(|cx| InputState::new(window, cx).placeholder("Enter desired name"));
let pass_input = cx.new(|cx| {
@@ -244,23 +238,25 @@ impl SidebarPanel {
onboarding_dialog::open(name_input, pass_input, repass_input, state, window, cx);
}
/// Show the Create Repository dialog.
fn open_create_repo(&mut self, window: &mut Window, cx: &mut Context<Self>) {
create_repo_dialog::open(self.dock_area.clone(), window, cx);
}
/// Open a repository's detail view in the dock's center.
fn open_repo(
&mut self,
announcement: &Announcement,
window: &mut Window,
cx: &mut Context<Self>,
) {
open_repo_panel(&self.dock_area, announcement, window, &mut *cx);
open_repo_panel(
&self.dock_area,
&announcement.addr(),
Some(announcement),
window,
&mut *cx,
);
}
/// Open a local repository's detail view in the dock's center.
///
/// The detail view offers to publish it to NIP-34.
fn open_local_repo(&mut self, path: PathBuf, window: &mut Window, cx: &mut Context<Self>) {
let detail =
@@ -324,7 +320,7 @@ impl SidebarPanel {
),
)
.map(|this| {
// Merged list, the user's NIP-34 repositories and local repositories discovered.
// The merged list: NIP-34 repositories first, then discovered local repositories.
let total = announcements.len() + local_repos.len();
if total == 0 {
@@ -364,7 +360,7 @@ impl SidebarPanel {
})
}
/// One row of the merged sidebar list, a NIP-34 or a local repository.
/// Renders row `ix` of the merged list: an announced repository or a local one.
fn render_repo_at(
&self,
announcements: &[Announcement],
@@ -393,7 +389,6 @@ impl SidebarPanel {
let avatar = PixelAvatar::new(format!("{}:{}", announcement.owner, announcement.id));
let announcement = announcement.clone();
// Badge with the unpushed commit count of the repository's local checkouts.
let unpushed = self
.unpushed
.get(&announcement.addr())
@@ -423,9 +418,7 @@ impl SidebarPanel {
)
}
/// One local repository row.
///
/// The directory name and a warning suffix, the repo is not yet set up for NIP-34.
/// A local repository that is not yet set up for NIP-34, marked with a warning.
fn render_local_row(&self, path: &Path, cx: &mut Context<Self>) -> impl IntoElement {
let name = path
.file_name()
@@ -445,12 +438,11 @@ impl SidebarPanel {
}))
}
/// Show the Import Identity dialog.
fn open_import(&mut self, window: &mut Window, cx: &mut Context<Self>) {
import_dialog::open(window, cx);
}
/// Render the user avatar and name in the sidebar, inside the titlebar drag area.
/// The user avatar and name, wired into the titlebar drag area.
fn render_user(
&self,
profile: &Profile,
@@ -480,7 +472,7 @@ impl SidebarPanel {
)
}
/// Sign-in placeholder shown while logged out.
/// Shown while no identity is signed in.
fn render_sign_in(&self, window: &mut Window, cx: &mut Context<Self>) -> Div {
v_flex()
.size_full()
@@ -599,9 +591,9 @@ impl Render for SidebarPanel {
}
v_flex()
.image_cache(gpui::retain_all("sidebar"))
.size_full()
.justify_between()
.image_cache(gpui::retain_all("sidebar"))
.bg(cx.theme().sidebar)
.text_color(cx.theme().sidebar_foreground)
.child(
@@ -9,10 +9,9 @@ use signed_state::Backend;
use crate::views::dialog_state::{DialogProgress, error_row};
/// Shared state for the Onboarding dialog, so async results can be rendered.
/// Progress of the onboarding flow, so async results can be rendered.
pub type OnboardingState = DialogProgress;
/// Open the Onboarding dialog for creating a new identity.
pub fn open(
name_input: Entity<InputState>,
pass_input: Entity<InputState>,
@@ -10,16 +10,14 @@ use signed_state::Backend;
use crate::views::dialog_state::{DialogProgress, error_row};
/// Shared state for the passphrase dialog, so async results can be rendered.
/// State of the passphrase dialog, so async results can be rendered.
#[derive(Default)]
pub struct PassphraseState {
/// Progress of the unlock flow.
pub progress: DialogProgress,
/// Keeps the Enter-to-submit subscription alive while the dialog is open.
_enter_subscription: Option<Subscription>,
}
/// Open the dialog asking for the passphrase that protects the stored identity.
pub fn open(window: &mut Window, cx: &mut App) {
let pass_input = cx.new(|cx| {
InputState::new(window, cx)
@@ -30,7 +28,7 @@ pub fn open(window: &mut Window, cx: &mut App) {
let handle = window.window_handle();
let state = cx.new(|_| PassphraseState::default());
// Enter in the passphrase field submits, same as the Unlock button.
// Enter in the passphrase field submits, like the Unlock button.
let enter_pass_input = pass_input.clone();
let enter_state = state.clone();
let enter_subscription = cx.subscribe(&pass_input, move |_input, event, cx| {
@@ -95,7 +93,6 @@ pub fn open(window: &mut Window, cx: &mut App) {
});
}
/// Submit the passphrase to the backend.
fn unlock(
pass_input: &Entity<InputState>,
state: &Entity<PassphraseState>,
@@ -22,7 +22,7 @@ use nostr::prelude::RelayUrl;
use settings::{AppearanceMode, Settings, SettingsStore};
use signed_ui::{SelectOption, setting_block, setting_row};
/// The index of `value` in `options`, for seeding a [`SelectState`].
/// Looks up the option index used to seed a [`SelectState`].
fn selected_index(options: &[SelectOption], value: &str) -> Option<IndexPath> {
options
.iter()
@@ -30,7 +30,7 @@ fn selected_index(options: &[SelectOption], value: &str) -> Option<IndexPath> {
.map(|row| IndexPath::default().row(row))
}
/// The light and dark themes registered in the theme registry.
/// Registered themes split into light and dark options, light first.
fn theme_options(cx: &App) -> (Vec<SelectOption>, Vec<SelectOption>) {
let registry = ThemeRegistry::global(cx);
let mut light = Vec::new();
@@ -48,7 +48,7 @@ fn theme_options(cx: &App) -> (Vec<SelectOption>, Vec<SelectOption>) {
(light, dark)
}
/// Stateful controls of the settings dialog, created once when it opens.
/// Created once when the dialog opens, so control state survives re-renders.
struct SettingsControls {
appearance: Entity<SelectState<Vec<SelectOption>>>,
light_theme: Entity<SelectState<Vec<SelectOption>>>,
@@ -58,9 +58,9 @@ struct SettingsControls {
radius: Entity<InputState>,
radius_lg: Entity<InputState>,
grasp_server_input: Entity<InputState>,
/// The effective default create-repository folder, shown in the disabled input.
/// The effective create-repository folder, shown in a disabled input.
default_folder: Entity<InputState>,
/// Keeps the control subscriptions alive for the dialog's lifetime.
/// Keeps the control subscriptions alive while the dialog is open.
_subscriptions: Vec<Subscription>,
}
@@ -264,7 +264,6 @@ impl SettingsControls {
}
}
/// Open the Settings dialog.
pub fn open(window: &mut Window, cx: &mut App) {
let controls = Rc::new(SettingsControls::new(window, cx));
@@ -278,8 +277,6 @@ pub fn open(window: &mut Window, cx: &mut App) {
});
}
/// The settings content, one section per related setting.
/// Sections are divided by horizontal separator lines.
fn settings_view(controls: &SettingsControls, cx: &mut App) -> impl IntoElement {
let store = SettingsStore::global(cx);
let settings = store.read(cx).settings().clone();
@@ -297,7 +294,6 @@ fn settings_view(controls: &SettingsControls, cx: &mut App) -> impl IntoElement
.child(repositories_section(&settings, controls, cx))
}
/// How the app picks its appearance.
fn appearance_section(controls: &SettingsControls, cx: &App) -> impl IntoElement {
v_flex().w_full().gap_3().child(setting_row(
cx,
@@ -307,7 +303,6 @@ fn appearance_section(controls: &SettingsControls, cx: &App) -> impl IntoElement
))
}
/// Theme configuration, the registry theme names plus tweaks the app customizes at startup.
fn theme_section(settings: &Settings, controls: &SettingsControls, cx: &App) -> impl IntoElement {
v_flex()
.gap_3()
@@ -378,7 +373,7 @@ fn theme_section(settings: &Settings, controls: &SettingsControls, cx: &App) ->
))
}
/// Default grasp servers offered until the user publishes a kind `10317` grasp list.
/// Default grasp servers, used until the user's kind `10317` grasp list loads.
fn grasp_servers_section(
settings: &Settings,
controls: &SettingsControls,
@@ -394,7 +389,6 @@ fn grasp_servers_section(
))
}
/// The editable list of default grasp servers plus an add-relay input.
/// Styled like the grasp-server section of the publish dialogs.
fn grasp_server_editor(
servers: &[String],
@@ -459,7 +453,7 @@ fn grasp_server_editor(
)
}
/// The bare host of a grasp server, defaults are entered without a scheme.
/// Shows only the host, since grasp servers are entered without a scheme.
/// Matches how the publish dialogs display servers.
fn display_server(server: &str) -> SharedString {
RelayUrl::parse(server)
@@ -469,7 +463,6 @@ fn display_server(server: &str) -> SharedString {
.unwrap_or_else(|| SharedString::from(server.to_owned()))
}
/// Local repository scanning and the create-repository dialog default folder.
fn repositories_section(
settings: &Settings,
controls: &SettingsControls,
@@ -494,7 +487,6 @@ fn repositories_section(
))
}
/// The editable list of scan directories plus an add-directory button.
/// Styled like the grasp-server list.
fn scan_paths_editor(scan_paths: &[PathBuf], cx: &App) -> impl IntoElement {
v_flex()
@@ -547,7 +539,6 @@ fn scan_paths_editor(scan_paths: &[PathBuf], cx: &App) -> impl IntoElement {
)
}
/// The default-folder selector, a disabled input plus a picker button.
/// Matches the create-repository dialog.
fn folder_selector(controls: &SettingsControls) -> impl IntoElement {
let default_folder = controls.default_folder.clone();
@@ -571,8 +562,7 @@ fn folder_selector(controls: &SettingsControls) -> impl IntoElement {
)
}
/// Parse the server input and append it to the default grasp servers.
/// A bare host is accepted.
/// Accepts a bare host as well as a full URL.
fn add_server(input: &Entity<InputState>, window: &mut Window, cx: &mut App) {
let value = input.read(cx).value().trim().to_owned();
if value.is_empty() {
@@ -604,7 +594,6 @@ fn add_server(input: &Entity<InputState>, window: &mut Window, cx: &mut App) {
input.update(cx, |input, cx| input.set_value("", window, cx));
}
/// Prompt for directories to add to the local-repository scan.
fn add_scan_path(cx: &mut App) {
let prompt = cx.prompt_for_paths(PathPromptOptions {
files: false,
@@ -641,8 +630,7 @@ fn add_scan_path(cx: &mut App) {
.detach();
}
/// Prompt for the Create Repository dialog's default folder.
/// Remember it in the settings and show it in the disabled input.
/// Persists the choice and reflects it in the disabled input.
fn choose_default_folder(default_folder: &Entity<InputState>, window: &mut Window, cx: &mut App) {
let handle = window.window_handle();
let default_folder = default_folder.clone();
@@ -676,9 +664,7 @@ fn choose_default_folder(default_folder: &Entity<InputState>, window: &mut Windo
.detach();
}
/// Wire a number input to the settings.
/// Step actions clamp and persist the value.
/// Typed changes parse, clamp and persist.
/// Step actions clamp and persist the value; typed changes parse, clamp and persist.
fn wire_number_input(
state: &Entity<InputState>,
subscriptions: &mut Vec<Subscription>,
@@ -751,7 +737,6 @@ fn wire_number_input(
}));
}
/// Apply the persisted appearance to the live theme.
fn apply_appearance(appearance: AppearanceMode, cx: &mut App) {
match appearance {
AppearanceMode::System => Theme::sync_system_appearance(None, cx),
@@ -760,7 +745,6 @@ fn apply_appearance(appearance: AppearanceMode, cx: &mut App) {
}
}
/// Re-apply the persisted theme configuration to the live theme.
fn apply_theme(cx: &mut App) {
let store = SettingsStore::global(cx);
let settings = store.read(cx).settings().theme.clone();