diff --git a/Cargo.lock b/Cargo.lock index 1675a2a..7b0016b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1259,7 +1259,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "gpui_util", "indexmap", @@ -1725,7 +1725,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "proc-macro2", "quote", @@ -3480,7 +3480,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "accesskit", "anyhow", @@ -3551,7 +3551,7 @@ dependencies = [ [[package]] name = "gpui-base" version = "0.5.2" -source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4" +source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" dependencies = [ "aho-corasick", "anyhow", @@ -3583,7 +3583,7 @@ dependencies = [ [[package]] name = "gpui-component" version = "0.5.2" -source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4" +source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" dependencies = [ "anyhow", "chrono", @@ -3663,7 +3663,7 @@ dependencies = [ [[package]] name = "gpui-component-assets" version = "0.5.1" -source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4" +source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" dependencies = [ "anyhow", "gpui", @@ -3677,7 +3677,7 @@ dependencies = [ [[package]] name = "gpui-component-macros" version = "0.5.1" -source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4" +source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" dependencies = [ "proc-macro2", "quote", @@ -3687,7 +3687,7 @@ dependencies = [ [[package]] name = "gpui_apple" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "block", @@ -3710,7 +3710,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "accesskit", "accesskit_unix", @@ -3756,7 +3756,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "accesskit", "accesskit_macos", @@ -3802,7 +3802,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3813,7 +3813,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "console_error_panic_hook", "gpui", @@ -3826,7 +3826,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "schemars", "serde", @@ -3836,7 +3836,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "log", @@ -3846,7 +3846,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "console_error_panic_hook", @@ -3870,7 +3870,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "bytemuck", @@ -3896,7 +3896,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "accesskit", "accesskit_windows", @@ -4209,7 +4209,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "async-compression", @@ -4229,7 +4229,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "rustls", "rustls-platform-verifier 0.5.3", @@ -5205,7 +5205,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "bindgen", @@ -6243,7 +6243,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "collections", "serde", @@ -7031,7 +7031,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "derive_refineable", ] @@ -7114,7 +7114,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "bytes", @@ -7526,7 +7526,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "async-task", "backtrace", @@ -8219,7 +8219,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "heapless 0.9.3", "log", @@ -9592,7 +9592,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "perf", "quote", @@ -10877,6 +10877,7 @@ dependencies = [ "gpui-component", "log", "nostr", + "paths", "signed_core", "signed_git", "signed_state", @@ -11390,7 +11391,7 @@ checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "anyhow", "chrono", @@ -11407,7 +11408,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" dependencies = [ "tracing", "tracing-subscriber", @@ -11418,7 +11419,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd" +source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" [[package]] name = "zune-core" diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 9d51d04..2de8abf 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -35,6 +35,12 @@ pub fn home_dir() -> PathBuf { dirs::home_dir().expect("failed to determine home directory") } +/// Returns the current user's Desktop folder, falling back to the home +/// directory (or an empty path) when it can't be determined. +pub fn desktop_dir() -> PathBuf { + dirs::desktop_dir().unwrap_or_else(|| dirs::home_dir().unwrap_or_default()) +} + /// Sets a custom directory for all user data, overriding the default data /// directory. Must be called before any other path operation. The directory /// is created if it doesn't exist and canonicalized to an absolute path. diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 3bceff5..4bef62f 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -7,6 +7,7 @@ publish.workspace = true [dependencies] assets = { path = "../assets" } dock = { workspace = true } +paths = { path = "../paths" } signed_core = { path = "../signed_core" } signed_git = { path = "../signed_git" } signed_state = { path = "../signed_state" } diff --git a/crates/workspace/src/views/sidebar/create_repo_dialog.rs b/crates/workspace/src/views/sidebar/create_repo_dialog.rs index 5e760a5..8710219 100644 --- a/crates/workspace/src/views/sidebar/create_repo_dialog.rs +++ b/crates/workspace/src/views/sidebar/create_repo_dialog.rs @@ -1,12 +1,11 @@ use dock::{DockArea, DockPlacement, panel_handle}; use gpui::prelude::*; -use gpui::{App, Entity, SharedString, WeakEntity, Window, div, px}; +use gpui::{App, Entity, PathPromptOptions, SharedString, WeakEntity, Window, div, px}; use gpui_base::input::TextareaState; -use gpui_component::button::{Button, ButtonVariants}; +use gpui_component::button::{Button, ButtonVariants, Toggle, ToggleVariants}; 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::tag::Tag; use gpui_component::{ActiveTheme, Disableable, IconName, Sizable, WindowExt, h_flex, v_flex}; use nostr::prelude::*; use signed_core::{Announcement, filters}; @@ -29,6 +28,8 @@ pub struct CreateRepoState { pub loading_servers: bool, pub error: Option, pub grasp_servers: Vec, + /// Whether the grasp server section is shown; defaults to shown. + pub servers_enabled: bool, } impl CreateRepoState { @@ -36,6 +37,7 @@ impl CreateRepoState { fn new_default() -> Self { Self { loading_servers: true, + servers_enabled: false, grasp_servers: DEFAULT_GRASP_SERVERS .iter() .filter_map(|url| RelayUrl::parse(url).ok()) @@ -57,6 +59,10 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) .auto_grow(3, 5) .placeholder("Short description") }); + let folder_input = cx.new(|cx| { + InputState::new(window, cx) + .default_value(paths::desktop_dir().to_string_lossy().to_string()) + }); let relay_input = cx.new(|cx| { InputState::new(window, cx).placeholder("wss://relay.example.com or relay.example.com") }); @@ -65,8 +71,14 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) load_user_grasp_servers(state.clone(), window, cx); window.open_dialog(cx, move |dialog, _window, _cx| { + const DESC: &str = "Publish a new repository to your grasp servers."; + const FOLDER_NOTE: &str = "Where the repository is stored, defaults to your Desktop"; + const SERVER_NOTE: &str = + "Where the repository is hosted, the initial push goes to each server"; + let name_input = name_input.clone(); let desc_input = desc_input.clone(); + let folder_input = folder_input.clone(); let relay_input = relay_input.clone(); let state = state.clone(); let dock_area = dock_area.clone(); @@ -79,15 +91,13 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) let error = state.read(cx).error.clone(); let servers = state.read(cx).grasp_servers.clone(); let loading_servers = state.read(cx).loading_servers; + let servers_enabled = state.read(cx).servers_enabled; content .child( DialogHeader::new() .child(DialogTitle::new().child("Create repository")) - .child( - DialogDescription::new() - .child("Publish a new repository to your grasp servers."), - ), + .child(DialogDescription::new().child(DESC)), ) .child( v_form() @@ -101,34 +111,86 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) .child( field() .label("Description") - .description("Optional") .child(Textarea::new(&desc_input)), ) + .child( + field().label("Folder").description(FOLDER_NOTE).child( + h_flex() + .gap_1() + .items_center() + .child( + div() + .flex_1() + .child(Input::new(&folder_input).disabled(true)), + ) + .child( + Button::new("choose-folder") + .icon(IconName::FolderOpen) + .ghost() + .tooltip("Choose folder") + .on_click({ + let folder_input = folder_input.clone(); + move |_ev, window, cx| { + choose_folder(&folder_input, window, cx); + } + }), + ), + ), + ) .child( field() - .label("Grasp servers") - .description( - "Where the repository is hosted; the initial push goes to each server", - ) - .child( - v_flex() - .gap_1() - .children( - servers - .iter() - .enumerate() - .map(|(ix, relay)| { - render_server_row(ix, relay, state.clone()) - }), - ) - .child( - h_flex().gap_1().items_center().child( + .label_fn({ + let state = state.clone(); + move |_window, cx| { + let enabled = state.read(cx).servers_enabled; + h_flex() + .w_full() + .justify_between() + .items_center() + .gap_1() + .child( + Toggle::new("grasp-servers-toggle") + .xsmall() + .ghost() + .icon({ + if enabled { + IconName::ChevronDown + } else { + IconName::ChevronUp + } + }) + .checked(enabled) + .on_click({ + let state = state.clone(); + move |checked, _window, cx| { + state.update(cx, |state, cx| { + state.servers_enabled = + *checked; + cx.notify(); + }); + } + }), + ) + .child(div().child("Grasp servers")) + } + }) + .when(servers_enabled, |this| this.description(SERVER_NOTE)) + .child(v_flex().gap_1().when(servers_enabled, |this| { + this.children(servers.iter().enumerate().map( + |(ix, relay)| { + render_server_row(ix, relay, state.clone(), cx) + }, + )) + .child( + h_flex() + .gap_1() + .items_center() + .child( div().flex_1().child(Input::new(&relay_input)), ) .child( Button::new("add-relay") .icon(IconName::Plus) - .small() .ghost() .tooltip("Add grasp server") .on_click({ @@ -144,16 +206,19 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) } }), ), - ) - .when(loading_servers, |this| { + ) + .when( + loading_servers, + |this| { this.child( div() .text_xs() .text_color(cx.theme().muted_foreground) .child("Loading your grasp servers…"), ) - }), - ), + }, + ) + })), ), ) .children(error.map(|message| { @@ -164,6 +229,7 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) Button::new("create") .primary() .label("Create repository") + .icon(IconName::ArrowRight) .tooltip("Create repository") .loading(busy) .disabled(busy) @@ -195,16 +261,26 @@ fn render_server_row( ix: usize, relay: &RelayUrl, state: Entity, + cx: &App, ) -> impl IntoElement { h_flex() .w_full() .gap_1() .items_center() - .child(Tag::new().flex_1().child(display_server(relay))) + .child( + h_flex() + .h_8() + .w_full() + .px_2() + .bg(cx.theme().muted) + .text_color(cx.theme().muted_foreground) + .text_sm() + .rounded(cx.theme().radius) + .child(display_server(relay)), + ) .child( Button::new(format!("remove-relay:{ix}")) .icon(IconName::Close) - .small() .ghost() .flex_shrink_0() .tooltip("Remove") @@ -227,6 +303,34 @@ fn display_server(relay: &RelayUrl) -> SharedString { .unwrap_or_else(|| SharedString::from(relay.to_string())) } +/// Prompt the user to pick the folder the repository will be stored in, using +/// the platform's native folder picker, and show the result in the disabled +/// folder input. +fn choose_folder(folder_input: &Entity, window: &mut Window, cx: &mut App) { + let handle = window.window_handle(); + let folder_input = folder_input.clone(); + + let prompt = cx.prompt_for_paths(PathPromptOptions { + files: false, + directories: true, + multiple: false, + prompt: Some("Choose folder".into()), + }); + + cx.spawn(async move |cx| { + if let Ok(Ok(Some(mut paths))) = prompt.await + && let Some(path) = paths.pop() + { + let path = path.to_string_lossy().to_string(); + cx.update_window(handle, |_, window, cx| { + folder_input.update(cx, |input, cx| input.set_value(path, window, cx)); + }) + .ok(); + } + }) + .detach(); +} + /// Parse the relay input (accepting a bare host) and append it to the list. fn add_relay( state: &Entity, diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 0000000..fa6fc2e --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,7 @@ +# TODO + +## Create repository dialog + +- [ ] Persist the user's preferred local repository folder (the one picked in + the create-repository dialog, defaulting to Desktop) and use it as the + default next time the dialog opens.