feat: allow choosing the default branch when publishing a repo
Rust / build (macos-latest, stable) (push) Waiting to run
Rust / build (ubuntu-latest, stable) (push) Waiting to run
Rust / build (windows-latest, stable) (push) Waiting to run

This commit is contained in:
2026-10-07 11:52:53 +07:00
parent 5887a14c50
commit 5586f67f0e
3 changed files with 117 additions and 38 deletions
+73 -2
View File
@@ -2,16 +2,19 @@ use std::path::PathBuf;
use assets::CustomIconName;
use gpui::prelude::*;
use gpui::{App, Entity, WeakEntity, Window, px};
use gpui::{App, Entity, SharedString, WeakEntity, Window, px};
use gpui_base::h_flex;
use gpui_base::input::TextareaState;
use gpui_component::button::{Button, ButtonVariants};
use gpui_component::dialog::{DialogDescription, DialogFooter, DialogHeader, DialogTitle};
use gpui_component::form::{field, v_form};
use gpui_component::input::{Input, InputState, Textarea};
use gpui_component::select::{Select, SelectState};
use gpui_component::{ActiveTheme, Disableable, WindowExt};
use settings::SettingsStore;
use signed_git::Repo;
use signed_state::Backend;
use signed_ui::SelectOption;
use super::RepoDetailView;
use crate::views::dialog_state::{DialogProgress, error_row};
@@ -48,8 +51,11 @@ pub fn open(
.auto_grow(3, 5)
.placeholder("Short description")
});
let branch_select: Entity<SelectState<Vec<SelectOption>>> =
cx.new(|cx| SelectState::new(Vec::new(), None, window, cx));
load_user_grasp_servers(grasp_state.clone(), window, cx);
load_branches(local_path.clone(), branch_select.clone(), window, cx);
window.open_dialog(cx, move |dialog, _window, _cx| {
const DESC: &str = "Publish this local repository to Nostr.";
@@ -57,6 +63,7 @@ pub fn open(
let name_input = name_input.clone();
let desc_input = desc_input.clone();
let relay_input = relay_input.clone();
let branch_select = branch_select.clone();
let state = state.clone();
let grasp_state = grasp_state.clone();
let local_path = local_path.clone();
@@ -105,6 +112,12 @@ pub fn open(
.child(local_path.display().to_string()),
),
)
.child(
field()
.label("Default branch")
.description("The local branch to announce as the default")
.child(Select::new(&branch_select).w_full()),
)
.child(grasp_servers_field(&grasp_state, &relay_input, cx)),
)
.children(error_row(&error, cx))
@@ -120,6 +133,7 @@ pub fn open(
.on_click({
let name_input = name_input.clone();
let desc_input = desc_input.clone();
let branch_select = branch_select.clone();
let state = state.clone();
let grasp_state = grasp_state.clone();
let local_path = local_path.clone();
@@ -129,6 +143,7 @@ pub fn open(
init_repository(
local_path.clone(),
(name_input.clone(), desc_input.clone()),
branch_select.clone(),
state.clone(),
grasp_state.clone(),
view.clone(),
@@ -143,9 +158,11 @@ pub fn open(
});
}
#[allow(clippy::too_many_arguments)]
fn init_repository(
local_path: PathBuf,
inputs: (Entity<InputState>, Entity<TextareaState>),
branch_select: Entity<SelectState<Vec<SelectOption>>>,
state: Entity<InitRepoState>,
grasp_state: Entity<GraspServersState>,
view: WeakEntity<RepoDetailView>,
@@ -156,6 +173,11 @@ fn init_repository(
let name = name_input.read(cx).value().trim().to_owned();
let description = desc_input.read(cx).value().trim().to_owned();
let servers = grasp_state.read(cx).grasp_servers.clone();
let default_branch = branch_select
.read(cx)
.selected_value()
.map(|value| value.trim().to_owned())
.filter(|branch| !branch.is_empty());
if name.is_empty() {
state.update(cx, |state, _| state.fail("Repository name is required"));
@@ -171,7 +193,14 @@ fn init_repository(
let backend = Backend::global(cx);
let task = backend.update(cx, |backend, cx| {
backend.publish_local_repo(local_path.clone(), &name, &description, servers, cx)
backend.publish_local_repo(
local_path.clone(),
&name,
&description,
servers,
default_branch,
cx,
)
});
let handle = window.window_handle();
@@ -199,3 +228,45 @@ fn init_repository(
})
.detach();
}
/// Fills the default-branch selector with the repository's local branches,
/// preselecting the checked-out one.
fn load_branches(
local_path: PathBuf,
select: Entity<SelectState<Vec<SelectOption>>>,
window: &mut Window,
cx: &mut App,
) {
let handle = window.window_handle();
cx.spawn(async move |cx| {
let loaded = cx
.background_spawn(async move {
let repo = Repo::open(&local_path)?;
let current: Option<SharedString> = repo.current_branch().map(Into::into);
Ok::<_, anyhow::Error>((repo.branches()?, current))
})
.await;
let _ = cx.update_window(handle, |_, window, cx| {
let Ok((branches, current)) = loaded.map_err(|error| {
log::warn!("failed to list branches for the default branch selector: {error:#}")
}) else {
return;
};
let options: Vec<SelectOption> = branches
.into_iter()
.map(|branch| SelectOption::new(branch.clone(), branch))
.collect();
select.update(cx, |state, cx| {
state.set_items(options, window, cx);
if let Some(current) = current.as_ref() {
state.set_selected_value(current, window, cx);
}
});
});
})
.detach();
}
+7 -5
View File
@@ -828,10 +828,6 @@ impl RepoDetailView {
}
/// Open the upstream repository, the `u` tag of this fork's announcement.
///
/// The announcement may not be in the local database yet. The panel opens
/// from the address and fills in when the store loads it; the store's
/// `subscribe_remote` fetches it from the bootstrap relays.
pub(super) fn open_upstream(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some(addr) = self
.announcement(cx)
@@ -1788,6 +1784,12 @@ impl RepoDetailView {
/// The Files tab body, or the clone/initial-load spinner.
fn render_files_tab(&self, cx: &mut Context<Self>) -> AnyElement {
if self.loading {
let label = if self.store.read(cx).path.is_some() {
"Loading repository..."
} else {
"Cloning repository..."
};
return v_flex()
.flex_1()
.size_full()
@@ -1799,7 +1801,7 @@ impl RepoDetailView {
div()
.text_sm()
.text_color(cx.theme().muted_foreground)
.child("Cloning repository..."),
.child(label),
)
.into_any_element();
}