diff --git a/Cargo.lock b/Cargo.lock index fca2507..07650a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,7 +163,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -338,7 +338,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -373,7 +373,7 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -504,7 +504,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -632,7 +632,7 @@ source = "git+https://github.com/kvark/blade?rev=b16f5c7bd873c7126f48c82c39e7ae6 dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -731,7 +731,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -800,7 +800,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.97", + "syn 2.0.98", "tempfile", "toml", ] @@ -957,7 +957,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -1039,7 +1039,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "indexmap", "rustc-hash 2.1.0", @@ -1314,7 +1314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -1339,13 +1339,13 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "proc-macro2", "quote", @@ -1412,7 +1412,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -1516,7 +1516,7 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -1765,7 +1765,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -1888,7 +1888,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -2058,7 +2058,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2092,8 +2092,8 @@ dependencies = [ "gpui_macros", "http_client", "image", + "inventory", "itertools 0.14.0", - "linkme", "log", "lyon", "media", @@ -2145,7 +2145,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "proc-macro2", "quote", @@ -2155,7 +2155,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "gpui", "tokio", @@ -2360,7 +2360,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "anyhow", "bytes", @@ -2575,7 +2575,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -2685,7 +2685,16 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", +] + +[[package]] +name = "inventory" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b12ebb6799019b044deaf431eadfe23245b259bba5a2c0796acec3943a3cdb" +dependencies = [ + "rustversion", ] [[package]] @@ -2860,26 +2869,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linkme" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566336154b9e58a4f055f6dd4cbab62c7dc0826ce3c0a04e63b2d2ecd784cdae" -dependencies = [ - "linkme-impl", -] - -[[package]] -name = "linkme-impl" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbe595006d355eaf9ae11db92707d4338cd2384d16866131cc1afdbdd35d8d9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.97", -] - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3022,7 +3011,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "anyhow", "bindgen", @@ -3346,7 +3335,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -3830,7 +3819,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -3865,7 +3854,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -3975,7 +3964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -4012,7 +4001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -4274,7 +4263,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "derive_refineable", ] @@ -4403,7 +4392,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "anyhow", "bytes", @@ -4480,7 +4469,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.97", + "syn 2.0.98", "walkdir", ] @@ -4668,7 +4657,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -4753,7 +4742,7 @@ checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "anyhow", "serde", @@ -4782,7 +4771,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -4793,7 +4782,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -4826,7 +4815,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5066,7 +5055,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5078,7 +5067,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "arrayvec", "log", @@ -5203,9 +5192,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.97" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dabd04e3b9a8c3c03d5e743f5ef5e1207befc9de704d477f7198cc28049763e" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -5238,7 +5227,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5369,7 +5358,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5380,7 +5369,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5501,7 +5490,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5635,7 +5624,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -5900,7 +5889,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#2f8237492641b4c97f61977a39f8a10225578a6d" +source = "git+https://github.com/zed-industries/zed#1dd2bbe2bae4e7340ffceb6a9be4742f571e9b1d" dependencies = [ "anyhow", "async-fs", @@ -6081,7 +6070,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -6116,7 +6105,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6334,7 +6323,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -6345,7 +6334,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -6718,7 +6707,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "synstructure", ] @@ -6805,7 +6794,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "zvariant_utils 2.1.0", ] @@ -6818,7 +6807,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "zbus_names 4.1.1", "zvariant 5.2.0", "zvariant_utils 3.1.0", @@ -6871,7 +6860,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -6891,7 +6880,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "synstructure", ] @@ -6912,7 +6901,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -6934,7 +6923,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -6999,7 +6988,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "zvariant_utils 2.1.0", ] @@ -7012,7 +7001,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", "zvariant_utils 3.1.0", ] @@ -7024,7 +7013,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.97", + "syn 2.0.98", ] [[package]] @@ -7037,6 +7026,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.97", + "syn 2.0.98", "winnow 0.6.26", ] diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index 6ac53d7..a0e97be 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -9,8 +9,8 @@ use common::{ profile::NostrProfile, }; use gpui::{ - actions, point, px, size, App, AppContext, Application, BorrowAppContext, Bounds, SharedString, - TitlebarOptions, WindowBounds, WindowKind, WindowOptions, + actions, point, px, size, App, AppContext, Application, BorrowAppContext, Bounds, Menu, + MenuItem, SharedString, TitlebarOptions, WindowBounds, WindowKind, WindowOptions, }; #[cfg(target_os = "linux")] use gpui::{WindowBackgroundAppearance, WindowDecorations}; @@ -223,8 +223,12 @@ fn main() { // Initialize components ui::init(cx); - // Set quit action + cx.activate(true); cx.on_action(quit); + cx.set_menus(vec![Menu { + name: "Coop".into(), + items: vec![MenuItem::action("Quit", Quit)], + }]); let opts = WindowOptions { #[cfg(not(target_os = "linux"))] @@ -248,7 +252,6 @@ fn main() { let window = cx .open_window(opts, |window, cx| { - cx.activate(true); window.set_window_title(APP_NAME); window.set_app_id(APP_ID); @@ -363,5 +366,5 @@ fn main() { } fn quit(_: &Quit, cx: &mut App) { - cx.shutdown(); + cx.quit(); } diff --git a/crates/app/src/views/app.rs b/crates/app/src/views/app.rs index 871f23c..75a99b4 100644 --- a/crates/app/src/views/app.rs +++ b/crates/app/src/views/app.rs @@ -133,11 +133,13 @@ impl AppView { } } PanelKind::Profile => { - let panel = Arc::new(profile::init(window, cx)); + if let Some(profile) = cx.global::().user() { + let panel = Arc::new(profile::init(profile, window, cx)); - self.dock.update(cx, |dock_area, cx| { - dock_area.add_panel(panel, action.position, window, cx); - }); + self.dock.update(cx, |dock_area, cx| { + dock_area.add_panel(panel, action.position, window, cx); + }); + } } PanelKind::Contacts => { let panel = Arc::new(contacts::init(window, cx)); diff --git a/crates/app/src/views/chat/mod.rs b/crates/app/src/views/chat/mod.rs index 57c8be9..f7645a6 100644 --- a/crates/app/src/views/chat/mod.rs +++ b/crates/app/src/views/chat/mod.rs @@ -370,8 +370,7 @@ impl Chat { }); // Show loading spinner - self.is_uploading = true; - cx.notify(); + self.set_loading(true, cx); // TODO: support multiple upload cx.spawn(move |this, mut async_cx| async move { @@ -394,8 +393,7 @@ impl Chat { // Stop loading spinner if let Some(view) = this.upgrade() { _ = async_cx.update_entity(&view, |this, cx| { - this.is_uploading = false; - cx.notify(); + this.set_loading(false, cx); }); } @@ -427,6 +425,11 @@ impl Chat { } }); } + + fn set_loading(&mut self, status: bool, cx: &mut Context) { + self.is_uploading = status; + cx.notify(); + } } impl Panel for Chat { diff --git a/crates/app/src/views/profile/mod.rs b/crates/app/src/views/profile/mod.rs index 1d58057..3209aab 100644 --- a/crates/app/src/views/profile/mod.rs +++ b/crates/app/src/views/profile/mod.rs @@ -1,18 +1,37 @@ +use std::str::FromStr; + +use async_utility::task::spawn; +use common::{constants::IMAGE_SERVICE, profile::NostrProfile, utils::nip96_upload}; use gpui::{ - div, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, - IntoElement, ParentElement, Render, SharedString, Styled, Window, + div, img, AnyElement, App, AppContext, Context, Entity, EventEmitter, Flatten, FocusHandle, + Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, Styled, Window, }; +use nostr_sdk::prelude::*; +use smol::fs; +use state::get_client; +use tokio::sync::oneshot; use ui::{ - button::Button, + button::{Button, ButtonVariants}, dock_area::panel::{Panel, PanelEvent}, + input::TextInput, popup_menu::PopupMenu, + ContextModal, Disableable, Sizable, Size, }; -pub fn init(window: &mut Window, cx: &mut App) -> Entity { - Profile::new(window, cx) +pub fn init(profile: NostrProfile, window: &mut Window, cx: &mut App) -> Entity { + Profile::new(profile, window, cx) } pub struct Profile { + profile: NostrProfile, + // Form + name_input: Entity, + avatar_input: Entity, + bio_input: Entity, + website_input: Entity, + is_loading: bool, + is_submitting: bool, + // Panel name: SharedString, closable: bool, zoomable: bool, @@ -20,14 +39,177 @@ pub struct Profile { } impl Profile { - pub fn new(_window: &mut Window, cx: &mut App) -> Entity { + pub fn new(mut profile: NostrProfile, window: &mut Window, cx: &mut App) -> Entity { + let name_input = cx.new(|cx| { + let mut input = TextInput::new(window, cx).text_size(Size::XSmall); + if let Some(name) = profile.metadata().display_name.as_ref() { + input.set_text(name, window, cx); + } + input + }); + let avatar_input = cx.new(|cx| { + let mut input = TextInput::new(window, cx).text_size(Size::XSmall).small(); + if let Some(picture) = profile.metadata().picture.as_ref() { + input.set_text(picture, window, cx); + } + input + }); + let bio_input = cx.new(|cx| { + let mut input = TextInput::new(window, cx) + .text_size(Size::XSmall) + .multi_line(); + if let Some(about) = profile.metadata().about.as_ref() { + input.set_text(about, window, cx); + } else { + input.set_placeholder("A short introduce about you."); + } + input + }); + let website_input = cx.new(|cx| { + let mut input = TextInput::new(window, cx).text_size(Size::XSmall); + if let Some(website) = profile.metadata().website.as_ref() { + input.set_text(website, window, cx); + } else { + input.set_placeholder("https://your-website.com"); + } + input + }); + cx.new(|cx| Self { + profile, + name_input, + avatar_input, + bio_input, + website_input, + is_loading: false, + is_submitting: false, name: "Profile".into(), closable: true, zoomable: true, focus_handle: cx.focus_handle(), }) } + + fn upload(&mut self, window: &mut Window, cx: &mut Context) { + let avatar_input = self.avatar_input.downgrade(); + let window_handle = window.window_handle(); + let paths = cx.prompt_for_paths(PathPromptOptions { + files: true, + directories: false, + multiple: false, + }); + + // Show loading spinner + self.set_loading(true, cx); + + cx.spawn(move |this, mut cx| async move { + match Flatten::flatten(paths.await.map_err(|e| e.into())) { + Ok(Some(mut paths)) => { + let path = paths.pop().unwrap(); + + if let Ok(file_data) = fs::read(path).await { + let (tx, rx) = oneshot::channel::(); + + spawn(async move { + let client = get_client(); + + if let Ok(url) = nip96_upload(client, file_data).await { + _ = tx.send(url); + } + }); + + if let Ok(url) = rx.await { + // Stop loading spinner + if let Some(view) = this.upgrade() { + cx.update_entity(&view, |this, cx| { + this.set_loading(false, cx); + }) + .unwrap(); + } + + // Update avatar input + if let Some(input) = avatar_input.upgrade() { + cx.update_window(window_handle, |_, window, cx| { + cx.update_entity(&input, |this, cx| { + this.set_text(url.to_string(), window, cx); + }); + }) + .unwrap(); + } + } + } + } + Ok(None) => {} + Err(_) => {} + } + }) + .detach(); + } + + fn set_loading(&mut self, status: bool, cx: &mut Context) { + self.is_loading = status; + cx.notify(); + } + + fn submit(&mut self, window: &mut Window, cx: &mut Context) { + // Show loading spinner + self.set_submitting(true, cx); + + let avatar = self.avatar_input.read(cx).text().to_string(); + let name = self.name_input.read(cx).text().to_string(); + let bio = self.bio_input.read(cx).text().to_string(); + let website = self.website_input.read(cx).text().to_string(); + + let mut new_metadata = self + .profile + .metadata() + .to_owned() + .display_name(name) + .about(bio); + + if let Ok(url) = Url::from_str(&avatar) { + new_metadata = new_metadata.picture(url); + }; + + if let Ok(url) = Url::from_str(&website) { + new_metadata = new_metadata.website(url); + } + + let window_handle = window.window_handle(); + + cx.spawn(|this, mut cx| async move { + let client = get_client(); + let (tx, rx) = oneshot::channel::(); + + cx.background_spawn(async move { + if let Ok(output) = client.set_metadata(&new_metadata).await { + _ = tx.send(output.val); + } + }) + .detach(); + + if rx.await.is_ok() { + if let Some(profile) = this.upgrade() { + cx.update_window(window_handle, |_, window, cx| { + cx.update_entity(&profile, |this, cx| { + this.set_submitting(false, cx); + window.push_notification( + "Your profile has been updated successfully", + cx, + ); + }) + }) + .unwrap(); + } + } + }) + .detach(); + } + + fn set_submitting(&mut self, status: bool, cx: &mut Context) { + self.is_submitting = status; + cx.notify(); + } } impl Panel for Profile { @@ -65,12 +247,91 @@ impl Focusable for Profile { } impl Render for Profile { - fn render(&mut self, _window: &mut gpui::Window, _cx: &mut Context) -> impl IntoElement { + fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context) -> impl IntoElement { div() .size_full() + .px_2() .flex() - .items_center() - .justify_center() - .child("Profile") + .flex_col() + .gap_3() + .child( + div() + .flex() + .flex_col() + .items_center() + .justify_end() + .gap_2() + .w_full() + .h_24() + .child( + img(format!( + "{}/?url={}&w=100&h=100&fit=cover&mask=circle&n=-1", + IMAGE_SERVICE, + self.avatar_input.read(cx).text() + )) + .size_10() + .rounded_full() + .flex_shrink_0(), + ) + .child( + div() + .flex() + .gap_1() + .items_center() + .w_full() + .child(self.avatar_input.clone()) + .child( + Button::new("upload") + .label("Upload") + .ghost() + .small() + .disabled(self.is_submitting) + .loading(self.is_loading) + .on_click(cx.listener(move |this, _, window, cx| { + this.upload(window, cx); + })), + ), + ), + ) + .child( + div() + .flex() + .flex_col() + .gap_1() + .text_xs() + .child("Name:") + .child(self.name_input.clone()), + ) + .child( + div() + .flex() + .flex_col() + .gap_1() + .text_xs() + .child("Bio:") + .child(self.bio_input.clone()), + ) + .child( + div() + .flex() + .flex_col() + .gap_1() + .text_xs() + .child("Website:") + .child(self.website_input.clone()), + ) + .child( + div().flex().items_center().justify_end().child( + Button::new("submit") + .label("Update") + .primary() + .small() + .disabled(self.is_loading) + .loading(self.is_submitting) + .on_click(cx.listener(move |this, _, window, cx| { + this.submit(window, cx); + })), + ), + ) } } diff --git a/crates/chat_state/src/room.rs b/crates/chat_state/src/room.rs index bf44271..cdc059c 100644 --- a/crates/chat_state/src/room.rs +++ b/crates/chat_state/src/room.rs @@ -79,12 +79,12 @@ impl Room { /// Set contact's metadata by public key pub fn set_metadata(&mut self, public_key: PublicKey, metadata: Metadata) { if self.owner.public_key() == public_key { - self.owner.metadata(&metadata); + self.owner.set_metadata(&metadata); } for member in self.members.iter_mut() { if member.public_key() == public_key { - member.metadata(&metadata); + member.set_metadata(&metadata); } } } diff --git a/crates/common/src/profile.rs b/crates/common/src/profile.rs index 3f98de8..f45f5a5 100644 --- a/crates/common/src/profile.rs +++ b/crates/common/src/profile.rs @@ -26,11 +26,6 @@ impl NostrProfile { self.public_key } - /// Set contact's metadata - pub fn metadata(&mut self, metadata: &Metadata) { - self.metadata = metadata.clone() - } - /// Get contact's avatar pub fn avatar(&self) -> String { if let Some(picture) = &self.metadata.picture { @@ -59,4 +54,14 @@ impl NostrProfile { shorted_public_key(self.public_key) } + + /// Get contact's metadata + pub fn metadata(&mut self) -> &Metadata { + &self.metadata + } + + /// Set contact's metadata + pub fn set_metadata(&mut self, metadata: &Metadata) { + self.metadata = metadata.clone() + } } diff --git a/crates/ui/src/dock_area/dock.rs b/crates/ui/src/dock_area/dock.rs index b1159c1..aa41e11 100644 --- a/crates/ui/src/dock_area/dock.rs +++ b/crates/ui/src/dock_area/dock.rs @@ -77,7 +77,7 @@ impl Dock { ) -> Self { let panel = cx.new(|cx| { let mut tab = TabPanel::new(None, dock_area.clone(), window, cx); - tab.closeable = false; + tab.closable = true; tab }); @@ -250,7 +250,7 @@ impl Dock { .when(self.placement.is_right(), |this| { this.cursor_col_resize() .top_0() - .left(px(1.)) + .left(px(-0.5)) .h_full() .w(HANDLE_SIZE) .pt_12() diff --git a/crates/ui/src/dock_area/tab_panel.rs b/crates/ui/src/dock_area/tab_panel.rs index 99c6894..92209d8 100644 --- a/crates/ui/src/dock_area/tab_panel.rs +++ b/crates/ui/src/dock_area/tab_panel.rs @@ -21,7 +21,7 @@ use std::sync::Arc; #[derive(Clone, Copy)] struct TabState { - closeable: bool, + closable: bool, zoomable: bool, draggable: bool, droppable: bool, @@ -70,7 +70,7 @@ pub struct TabPanel { pub(crate) active_ix: usize, /// If this is true, the Panel closeable will follow the active panel's closeable, /// otherwise this TabPanel will not able to close - pub(crate) closeable: bool, + pub(crate) closable: bool, tab_bar_scroll_handle: ScrollHandle, is_zoomed: bool, is_collapsed: bool, @@ -90,7 +90,7 @@ impl Panel for TabPanel { } fn closable(&self, cx: &App) -> bool { - if !self.closeable { + if !self.closable { return false; } @@ -139,7 +139,7 @@ impl TabPanel { will_split_placement: None, is_zoomed: false, is_collapsed: false, - closeable: true, + closable: true, } } @@ -356,8 +356,6 @@ impl TabPanel { let view = cx.entity().clone(); let build_popup_menu = move |this, cx: &App| view.read(cx).popup_menu(this, cx); - // TODO: Do not show MenuButton if there is no menu items - h_flex() .gap_2() .occlude() @@ -390,7 +388,7 @@ impl TabPanel { let name = if is_zoomed { "Zoom Out" } else { "Zoom In" }; this.separator().menu(name, Box::new(ToggleZoom)) }) - .when(state.closeable, |this| { + .when(state.closable, |this| { this.separator().menu("Close", Box::new(ClosePanel)) }) }) @@ -1015,14 +1013,14 @@ impl Render for TabPanel { let focus_handle = self.focus_handle(cx); let mut state = TabState { - closeable: self.closable(cx), + closable: self.closable(cx), draggable: self.draggable(cx), droppable: self.droppable(cx), zoomable: self.zoomable(cx), }; if !state.draggable { - state.closeable = false; + state.closable = false; } v_flex() diff --git a/crates/ui/src/input/element.rs b/crates/ui/src/input/element.rs index e019404..5b4f513 100644 --- a/crates/ui/src/input/element.rs +++ b/crates/ui/src/input/element.rs @@ -495,7 +495,7 @@ impl Element for TextElement { // Paint selections if let Some(path) = prepaint.selection_path.take() { - window.paint_path(path, cx.theme().accent.step(cx, ColorScaleStep::FIVE)); + window.paint_path(path, cx.theme().accent.step(cx, ColorScaleStep::FOUR)); } // Paint multi line text @@ -515,7 +515,7 @@ impl Element for TextElement { for line in prepaint.lines.iter() { let p = point(origin.x, origin.y + offset_y); - _ = line.paint(p, line_height, window, cx); + _ = line.paint(p, line_height, gpui::TextAlign::Left, window, cx); offset_y += line.size(line_height).height; } diff --git a/crates/ui/src/notification.rs b/crates/ui/src/notification.rs index aa8014d..6039e96 100644 --- a/crates/ui/src/notification.rs +++ b/crates/ui/src/notification.rs @@ -237,16 +237,16 @@ impl Render for Notification { .shadow_md() .p_2() .gap_3() - .child(div().absolute().top_3().left_2().child(icon)) + .child(div().absolute().top_2p5().left_2().child(icon)) .child( v_flex() .pl_6() .gap_1() .when_some(self.title.clone(), |this, title| { - this.child(div().text_sm().font_semibold().child(title)) + this.child(div().text_xs().font_semibold().child(title)) }) .overflow_hidden() - .child(div().text_sm().child(self.message.clone())), + .child(div().text_xs().child(self.message.clone())), ) .when_some(self.on_click.clone(), |this, on_click| { this.cursor_pointer() @@ -370,16 +370,16 @@ impl Render for NotificationList { .child( v_flex() .id("notification-list") + .gap_3() .absolute() .relative() .right_0() .h(size.height - px(8.)) + .children(items) .on_hover(cx.listener(|view, hovered, _window, cx| { view.expanded = *hovered; cx.notify(); - })) - .gap_3() - .children(items), + })), ) } }