From a30f2dcc8ad2eafa5205cc5c14611f09e2715382 Mon Sep 17 00:00:00 2001 From: reya <123083837+reyamir@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:43:07 +0700 Subject: [PATCH] refactor ui (#17) * wip: redesign sidebar * wip: adjust dpi * update * update * refactor modal * fix modal --- Cargo.lock | 76 ++--- Packager.toml | 10 +- assets/icons/address-book.svg | 3 + assets/icons/arrows-in.svg | 1 + assets/icons/caret-down-fill.svg | 1 + ...{chevron-down-small.svg => caret-down.svg} | 2 +- assets/icons/caret-right.svg | 1 + assets/icons/caret-up.svg | 1 + assets/icons/check-circle-fill.svg | 1 + assets/icons/check-circle.svg | 1 + assets/icons/check.svg | 1 + assets/icons/chevron-down.svg | 3 - assets/icons/circle-check.svg | 3 - .../{circle-x.svg => close-circle-fill.svg} | 0 assets/icons/close-circle.svg | 3 + assets/icons/close.svg | 4 +- assets/icons/compose-fill.svg | 3 - assets/icons/explore.svg | 3 - assets/icons/folder-fill.svg | 3 - assets/icons/folder-open-fill.svg | 3 - assets/icons/group-fill.svg | 3 - assets/icons/group.svg | 3 - assets/icons/mailbox-fill.svg | 1 + assets/icons/messages.svg | 4 - assets/icons/plus-circle-fill.svg | 1 + assets/icons/upload.svg | 2 +- assets/icons/users-three-fill.svg | 1 + crates/chats/src/room.rs | 4 +- crates/common/src/profile.rs | 2 +- crates/coop/src/chat_space.rs | 237 ++++++--------- crates/coop/src/main.rs | 2 +- crates/coop/src/views/chat.rs | 67 ++--- .../coop/src/views/{sidebar => }/compose.rs | 168 ++++------- crates/coop/src/views/contacts.rs | 71 +++-- crates/coop/src/views/login.rs | 35 +-- crates/coop/src/views/mod.rs | 2 +- crates/coop/src/views/new_account.rs | 39 +-- crates/coop/src/views/onboarding.rs | 11 +- crates/coop/src/views/profile.rs | 179 +++++------ crates/coop/src/views/relays.rs | 236 ++++++++------- crates/coop/src/views/settings.rs | 76 ----- crates/coop/src/views/sidebar/button.rs | 58 ++++ crates/coop/src/views/sidebar/folder.rs | 125 ++++---- crates/coop/src/views/sidebar/mod.rs | 283 +++++++++++------- crates/coop/src/views/welcome.rs | 6 +- crates/ui/src/button.rs | 5 +- crates/ui/src/dock_area/tab_panel.rs | 54 +--- crates/ui/src/dropdown.rs | 4 +- crates/ui/src/icon.rs | 118 +++----- crates/ui/src/indicator.rs | 2 +- crates/ui/src/input/input.rs | 1 + crates/ui/src/modal.rs | 92 +++--- crates/ui/src/notification.rs | 7 +- crates/ui/src/popup_menu.rs | 4 +- crates/ui/src/styled.rs | 7 +- crates/ui/src/tab/mod.rs | 23 +- crates/ui/src/theme/mod.rs | 2 +- crates/ui/src/title_bar.rs | 8 +- 58 files changed, 899 insertions(+), 1167 deletions(-) create mode 100644 assets/icons/address-book.svg create mode 100644 assets/icons/arrows-in.svg create mode 100644 assets/icons/caret-down-fill.svg rename assets/icons/{chevron-down-small.svg => caret-down.svg} (67%) create mode 100644 assets/icons/caret-right.svg create mode 100644 assets/icons/caret-up.svg create mode 100644 assets/icons/check-circle-fill.svg create mode 100644 assets/icons/check-circle.svg create mode 100644 assets/icons/check.svg delete mode 100644 assets/icons/chevron-down.svg delete mode 100644 assets/icons/circle-check.svg rename assets/icons/{circle-x.svg => close-circle-fill.svg} (100%) create mode 100644 assets/icons/close-circle.svg delete mode 100644 assets/icons/compose-fill.svg delete mode 100644 assets/icons/explore.svg delete mode 100644 assets/icons/folder-fill.svg delete mode 100644 assets/icons/folder-open-fill.svg delete mode 100644 assets/icons/group-fill.svg delete mode 100644 assets/icons/group.svg create mode 100644 assets/icons/mailbox-fill.svg delete mode 100644 assets/icons/messages.svg create mode 100644 assets/icons/plus-circle-fill.svg create mode 100644 assets/icons/users-three-fill.svg rename crates/coop/src/views/{sidebar => }/compose.rs (81%) delete mode 100644 crates/coop/src/views/settings.rs create mode 100644 crates/coop/src/views/sidebar/button.rs diff --git a/Cargo.lock b/Cargo.lock index 225d5c5..c4ef785 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arbitrary" @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1524,7 +1524,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "proc-macro2", "quote", @@ -2306,7 +2306,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2397,7 +2397,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "proc-macro2", "quote", @@ -2413,9 +2413,9 @@ checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" [[package]] name = "h2" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ "atomic-waker", "bytes", @@ -2621,7 +2621,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "anyhow", "bytes", @@ -2638,7 +2638,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3176,9 +3176,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libfuzzer-sys" @@ -3379,7 +3379,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "anyhow", "bindgen 0.71.1", @@ -3576,8 +3576,8 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" -version = "0.40.0" -source = "git+https://github.com/rust-nostr/nostr#f4f3f50aa637b3d288a6f1cf762260005b7b498a" +version = "0.41.0" +source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" dependencies = [ "aes", "base64", @@ -3601,8 +3601,8 @@ dependencies = [ [[package]] name = "nostr-connect" -version = "0.40.0" -source = "git+https://github.com/rust-nostr/nostr#f4f3f50aa637b3d288a6f1cf762260005b7b498a" +version = "0.41.0" +source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" dependencies = [ "async-utility", "nostr", @@ -3613,8 +3613,8 @@ dependencies = [ [[package]] name = "nostr-database" -version = "0.40.0" -source = "git+https://github.com/rust-nostr/nostr#f4f3f50aa637b3d288a6f1cf762260005b7b498a" +version = "0.41.0" +source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" dependencies = [ "flatbuffers", "lru", @@ -3624,8 +3624,8 @@ dependencies = [ [[package]] name = "nostr-lmdb" -version = "0.40.0" -source = "git+https://github.com/rust-nostr/nostr#f4f3f50aa637b3d288a6f1cf762260005b7b498a" +version = "0.41.0" +source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" dependencies = [ "async-utility", "heed", @@ -3637,8 +3637,8 @@ dependencies = [ [[package]] name = "nostr-relay-pool" -version = "0.40.0" -source = "git+https://github.com/rust-nostr/nostr#f4f3f50aa637b3d288a6f1cf762260005b7b498a" +version = "0.41.0" +source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" dependencies = [ "async-utility", "async-wsocket", @@ -3654,8 +3654,8 @@ dependencies = [ [[package]] name = "nostr-sdk" -version = "0.40.0" -source = "git+https://github.com/rust-nostr/nostr#f4f3f50aa637b3d288a6f1cf762260005b7b498a" +version = "0.41.0" +source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" dependencies = [ "async-utility", "nostr", @@ -4464,9 +4464,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -4729,9 +4729,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" dependencies = [ "avif-serialize", "imgref", @@ -4813,7 +4813,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "derive_refineable", "workspace-hack", @@ -4951,7 +4951,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "anyhow", "bytes", @@ -5421,7 +5421,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "anyhow", "serde", @@ -5715,18 +5715,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5744,7 +5744,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "arrayvec", "log", @@ -6646,7 +6646,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#b864a9b0ae633006a44c02b723fe6fad07e84b93" +source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" dependencies = [ "anyhow", "async-fs", diff --git a/Packager.toml b/Packager.toml index 3812c2b..91faf5c 100644 --- a/Packager.toml +++ b/Packager.toml @@ -1,8 +1,13 @@ name = "coop" description = "Coop is a cross-platform Nostr client designed for secure communication focus on simplicity and customizability." product-name = "Coop" +version = "0.1.4" +category = "SocialNetworking" identifier = "su.reya.coop" resources = ["assets/*/*", "Cargo.toml", "./LICENSE", "./README.md"] +binaries = [ { path = "coop", main = true } ] +before-packaging-command = "cargo build --release" +out-dir = "./target/release" icons = [ "crates/coop/resources/32x32.png", "crates/coop/resources/128x128.png", @@ -11,8 +16,3 @@ icons = [ "crates/coop/resources/app-icon.png", "crates/coop/resources/app-icon.ico", ] -before-packaging-command = "cargo build --release" -out-dir = "./target/release" -binaries = [ - { path = "coop", main = true }, -] diff --git a/assets/icons/address-book.svg b/assets/icons/address-book.svg new file mode 100644 index 0000000..4b061d6 --- /dev/null +++ b/assets/icons/address-book.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/arrows-in.svg b/assets/icons/arrows-in.svg new file mode 100644 index 0000000..46904e4 --- /dev/null +++ b/assets/icons/arrows-in.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/caret-down-fill.svg b/assets/icons/caret-down-fill.svg new file mode 100644 index 0000000..48d34e0 --- /dev/null +++ b/assets/icons/caret-down-fill.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/chevron-down-small.svg b/assets/icons/caret-down.svg similarity index 67% rename from assets/icons/chevron-down-small.svg rename to assets/icons/caret-down.svg index ca53e93..8de2648 100644 --- a/assets/icons/chevron-down-small.svg +++ b/assets/icons/caret-down.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/icons/caret-right.svg b/assets/icons/caret-right.svg new file mode 100644 index 0000000..f86630d --- /dev/null +++ b/assets/icons/caret-right.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/caret-up.svg b/assets/icons/caret-up.svg new file mode 100644 index 0000000..09e8ccd --- /dev/null +++ b/assets/icons/caret-up.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/check-circle-fill.svg b/assets/icons/check-circle-fill.svg new file mode 100644 index 0000000..e776f4f --- /dev/null +++ b/assets/icons/check-circle-fill.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/check-circle.svg b/assets/icons/check-circle.svg new file mode 100644 index 0000000..4889d9c --- /dev/null +++ b/assets/icons/check-circle.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/check.svg b/assets/icons/check.svg new file mode 100644 index 0000000..5b34ba7 --- /dev/null +++ b/assets/icons/check.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/chevron-down.svg b/assets/icons/chevron-down.svg deleted file mode 100644 index 944e76e..0000000 --- a/assets/icons/chevron-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/circle-check.svg b/assets/icons/circle-check.svg deleted file mode 100644 index 797bdc0..0000000 --- a/assets/icons/circle-check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/circle-x.svg b/assets/icons/close-circle-fill.svg similarity index 100% rename from assets/icons/circle-x.svg rename to assets/icons/close-circle-fill.svg diff --git a/assets/icons/close-circle.svg b/assets/icons/close-circle.svg new file mode 100644 index 0000000..9e7b90f --- /dev/null +++ b/assets/icons/close-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/close.svg b/assets/icons/close.svg index 3469a2e..55af35f 100644 --- a/assets/icons/close.svg +++ b/assets/icons/close.svg @@ -1,3 +1 @@ - - - + diff --git a/assets/icons/compose-fill.svg b/assets/icons/compose-fill.svg deleted file mode 100644 index 4d4b4e0..0000000 --- a/assets/icons/compose-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/explore.svg b/assets/icons/explore.svg deleted file mode 100644 index 69bcd9b..0000000 --- a/assets/icons/explore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/folder-fill.svg b/assets/icons/folder-fill.svg deleted file mode 100644 index febae36..0000000 --- a/assets/icons/folder-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/folder-open-fill.svg b/assets/icons/folder-open-fill.svg deleted file mode 100644 index 834720f..0000000 --- a/assets/icons/folder-open-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/group-fill.svg b/assets/icons/group-fill.svg deleted file mode 100644 index e8e3704..0000000 --- a/assets/icons/group-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/group.svg b/assets/icons/group.svg deleted file mode 100644 index 7f94e3c..0000000 --- a/assets/icons/group.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/mailbox-fill.svg b/assets/icons/mailbox-fill.svg new file mode 100644 index 0000000..a903d0d --- /dev/null +++ b/assets/icons/mailbox-fill.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/messages.svg b/assets/icons/messages.svg deleted file mode 100644 index fe6207c..0000000 --- a/assets/icons/messages.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/plus-circle-fill.svg b/assets/icons/plus-circle-fill.svg new file mode 100644 index 0000000..848c240 --- /dev/null +++ b/assets/icons/plus-circle-fill.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/upload.svg b/assets/icons/upload.svg index c436e83..448cd21 100644 --- a/assets/icons/upload.svg +++ b/assets/icons/upload.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/icons/users-three-fill.svg b/assets/icons/users-three-fill.svg new file mode 100644 index 0000000..89b75f3 --- /dev/null +++ b/assets/icons/users-three-fill.svg @@ -0,0 +1 @@ + diff --git a/crates/chats/src/room.rs b/crates/chats/src/room.rs index fea117f..9944af1 100644 --- a/crates/chats/src/room.rs +++ b/crates/chats/src/room.rs @@ -152,7 +152,9 @@ impl Room { /// The Profile of the first member in the room pub fn first_member(&self, cx: &App) -> Profile { let account = Account::global(cx).read(cx); - let profile = account.profile.clone().unwrap(); + let Some(profile) = account.profile.clone() else { + return self.profile_by_pubkey(&self.members[0], cx); + }; if let Some(public_key) = self .members diff --git a/crates/common/src/profile.rs b/crates/common/src/profile.rs index fa63cb5..32c9d9a 100644 --- a/crates/common/src/profile.rs +++ b/crates/common/src/profile.rs @@ -15,7 +15,7 @@ impl SharedProfile for Profile { .filter(|picture| !picture.is_empty()) .map(|picture| { format!( - "{}/?url={}&w=100&h=100&fit=cover&mask=circle&n=-1", + "{}/?url={}&w=100&h=100&fit=cover&mask=circle&n=-1&default=npub1zfss807aer0j26mwp2la0ume0jqde3823rmu97ra6sgyyg956e0s6xw445.blossom.band/c30703b48f511c293a9003be8100cdad37b8798b77a1dc3ec6eb8a20443d5dea.png", IMAGE_SERVICE, picture ) .into() diff --git a/crates/coop/src/chat_space.rs b/crates/coop/src/chat_space.rs index fb06f83..6b33bcf 100644 --- a/crates/coop/src/chat_space.rs +++ b/crates/coop/src/chat_space.rs @@ -1,31 +1,47 @@ use account::Account; -use common::profile::SharedProfile; -use global::get_client; use gpui::{ - actions, div, img, impl_internal_actions, prelude::FluentBuilder, px, App, AppContext, Axis, - Context, Entity, InteractiveElement, IntoElement, ParentElement, Render, Styled, Subscription, - Task, Window, + div, impl_internal_actions, prelude::FluentBuilder, px, App, AppContext, Axis, Context, Entity, + InteractiveElement, IntoElement, ParentElement, Render, Styled, Subscription, Window, }; use serde::Deserialize; use smallvec::{smallvec, SmallVec}; use std::sync::Arc; use ui::{ - button::{Button, ButtonRounded, ButtonVariants}, + button::{Button, ButtonVariants}, dock_area::{dock::DockPlacement, panel::PanelView, DockArea, DockItem}, - popup_menu::PopupMenuExt, - theme::{scale::ColorScaleStep, ActiveTheme, Appearance, Theme}, - ContextModal, Icon, IconName, Root, Sizable, TitleBar, + theme::{ActiveTheme, Appearance, Theme}, + ContextModal, IconName, Root, Sizable, TitleBar, }; -use crate::views::{chat, contacts, profile, relays, settings, welcome}; +use crate::views::{chat, compose, contacts, profile, relays, welcome}; use crate::views::{onboarding, sidebar}; +const MODAL_WIDTH: f32 = 420.; +const SIDEBAR_WIDTH: f32 = 280.; + +impl_internal_actions!(dock, [AddPanel, ToggleModal]); + +pub fn init(window: &mut Window, cx: &mut App) -> Entity { + ChatSpace::new(window, cx) +} + #[derive(Clone, PartialEq, Eq, Deserialize)] pub enum PanelKind { Room(u64), + // More kind will be added here +} + +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub enum ModalKind { Profile, - Contacts, - Settings, + Compose, + Contact, + Relay, +} + +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub struct ToggleModal { + pub modal: ModalKind, } #[derive(Clone, PartialEq, Eq, Deserialize)] @@ -40,16 +56,6 @@ impl AddPanel { } } -// Dock actions -impl_internal_actions!(dock, [AddPanel]); - -// Account actions -actions!(account, [Logout]); - -pub fn init(window: &mut Window, cx: &mut App) -> Entity { - ChatSpace::new(window, cx) -} - pub struct ChatSpace { titlebar: bool, dock: Entity, @@ -61,23 +67,26 @@ impl ChatSpace { pub fn new(window: &mut Window, cx: &mut App) -> Entity { let account = Account::global(cx); let dock = cx.new(|cx| DockArea::new(window, cx)); - let titlebar = false; cx.new(|cx| { + let mut subscriptions = smallvec![]; + + subscriptions.push(cx.observe_in( + &account, + window, + |this: &mut ChatSpace, account, window, cx| { + if account.read(cx).profile.is_some() { + this.open_chats(window, cx); + } else { + this.open_onboarding(window, cx); + } + }, + )); + let mut this = Self { dock, - titlebar, - subscriptions: smallvec![cx.observe_in( - &account, - window, - |this: &mut ChatSpace, account, window, cx| { - if account.read(cx).profile.is_some() { - this.open_chats(window, cx); - } else { - this.open_onboarding(window, cx); - } - }, - )], + subscriptions, + titlebar: false, }; if Account::global(cx).read(cx).profile.is_some() { @@ -135,7 +144,7 @@ impl ChatSpace { ); self.dock.update(cx, |this, cx| { - this.set_left_dock(left, Some(px(260.)), true, window, cx); + this.set_left_dock(left, Some(px(SIDEBAR_WIDTH)), true, window, cx); this.set_center(center, window, cx); }); } @@ -165,74 +174,6 @@ impl ChatSpace { })) } - fn render_account_btn(&self, cx: &mut Context) -> impl IntoElement { - Button::new("account") - .ghost() - .xsmall() - .reverse() - .icon(Icon::new(IconName::ChevronDownSmall)) - .when_some( - Account::global(cx).read(cx).profile.as_ref(), - |this, profile| this.child(img(profile.shared_avatar()).size_5()), - ) - .popup_menu(move |this, _, _cx| { - this.menu( - "Profile", - Box::new(AddPanel::new(PanelKind::Profile, DockPlacement::Right)), - ) - .menu( - "Contacts", - Box::new(AddPanel::new(PanelKind::Contacts, DockPlacement::Right)), - ) - .menu( - "Settings", - Box::new(AddPanel::new(PanelKind::Settings, DockPlacement::Center)), - ) - .separator() - .menu("Change account", Box::new(Logout)) - }) - } - - fn render_relays_btn(&self, cx: &mut Context) -> impl IntoElement { - Button::new("relays") - .xsmall() - .ghost() - .icon(IconName::Relays) - .on_click(cx.listener(|this, _, window, cx| { - this.render_edit_relays(window, cx); - })) - } - - fn render_edit_relays(&self, window: &mut Window, cx: &mut Context) { - let relays = relays::init(window, cx); - - window.open_modal(cx, move |this, window, cx| { - let is_loading = relays.read(cx).loading(); - - this.width(px(420.)) - .title("Edit your Messaging Relays") - .child(relays.clone()) - .footer( - div() - .p_2() - .border_t_1() - .border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE)) - .child( - Button::new("update_inbox_relays_btn") - .label("Update") - .primary() - .bold() - .rounded(ButtonRounded::Large) - .w_full() - .loading(is_loading) - .on_click(window.listener_for(&relays, |this, _, window, cx| { - this.update(window, cx); - })), - ), - ) - }); - } - fn on_panel_action(&mut self, action: &AddPanel, window: &mut Window, cx: &mut Context) { match &action.panel { PanelKind::Room(id) => { @@ -246,49 +187,55 @@ impl ChatSpace { Err(e) => window.push_notification(e.to_string(), cx), } } - PanelKind::Profile => { - let panel = profile::init(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)); - - self.dock.update(cx, |dock_area, cx| { - dock_area.add_panel(panel, action.position, window, cx); - }); - } - PanelKind::Settings => { - let panel = Arc::new(settings::init(window, cx)); - - self.dock.update(cx, |dock_area, cx| { - dock_area.add_panel(panel, action.position, window, cx); - }); - } }; } - fn on_logout_action(&mut self, _action: &Logout, window: &mut Window, cx: &mut Context) { - let client = get_client(); - let reset: Task> = cx.background_spawn(async move { - client.reset().await; - Ok(()) - }); + fn on_modal_action( + &mut self, + action: &ToggleModal, + window: &mut Window, + cx: &mut Context, + ) { + match action.modal { + ModalKind::Profile => { + let profile = profile::init(window, cx); - cx.spawn_in(window, async move |_, cx| { - if reset.await.is_ok() { - cx.update(|_, cx| { - Account::global(cx).update(cx, |this, cx| { - this.profile = None; - cx.notify(); - }); + window.open_modal(cx, move |modal, _, _| { + modal + .title("Profile") + .width(px(MODAL_WIDTH)) + .child(profile.clone()) }) - .ok(); - }; - }) - .detach(); + } + ModalKind::Compose => { + let compose = compose::init(window, cx); + + window.open_modal(cx, move |modal, _, _| { + modal + .title("Direct Messages") + .width(px(MODAL_WIDTH)) + .child(compose.clone()) + }) + } + ModalKind::Contact => { + let contacts = contacts::init(window, cx); + + window.open_modal(cx, move |this, _window, _cx| { + this.width(px(MODAL_WIDTH)) + .title("Contacts") + .child(contacts.clone()) + }); + } + ModalKind::Relay => { + let relays = relays::init(window, cx); + + window.open_modal(cx, move |this, _, _| { + this.width(px(MODAL_WIDTH)) + .title("Edit your Messaging Relays") + .child(relays.clone()) + }); + } + }; } } @@ -319,9 +266,7 @@ impl Render for ChatSpace { .justify_end() .gap_2() .px_2() - .child(self.render_appearance_btn(cx)) - .child(self.render_relays_btn(cx)) - .child(self.render_account_btn(cx)), + .child(self.render_appearance_btn(cx)), ), ) }) @@ -334,6 +279,6 @@ impl Render for ChatSpace { .children(modal_layer) // Actions .on_action(cx.listener(Self::on_panel_action)) - .on_action(cx.listener(Self::on_logout_action)) + .on_action(cx.listener(Self::on_modal_action)) } } diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 3310215..679220b 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -261,7 +261,7 @@ fn main() { }), window_bounds: Some(WindowBounds::Windowed(Bounds::centered( None, - size(px(900.0), px(680.0)), + size(px(920.0), px(700.0)), cx, ))), #[cfg(target_os = "linux")] diff --git a/crates/coop/src/views/chat.rs b/crates/coop/src/views/chat.rs index bca717d..6490228 100644 --- a/crates/coop/src/views/chat.rs +++ b/crates/coop/src/views/chat.rs @@ -14,14 +14,14 @@ use smallvec::{smallvec, SmallVec}; use smol::fs; use std::{collections::HashMap, sync::Arc}; use ui::{ - button::{Button, ButtonRounded, ButtonVariants}, + button::{Button, ButtonVariants}, dock_area::panel::{Panel, PanelEvent}, input::{InputEvent, TextInput}, notification::Notification, popup_menu::PopupMenu, text::RichText, theme::{scale::ColorScaleStep, ActiveTheme}, - v_flex, ContextModal, Icon, IconName, Sizable, StyledExt, + v_flex, ContextModal, Disableable, Icon, IconName, Size, StyledExt, }; const ALERT: &str = "has not set up Messaging (DM) Relays, so they will NOT receive your messages."; @@ -58,8 +58,7 @@ impl Chat { let attaches = cx.new(|_| None); let input = cx.new(|cx| { TextInput::new(window, cx) - .appearance(false) - .text_size(ui::Size::Small) + .text_size(Size::Small) .placeholder("Message...") }); @@ -343,11 +342,12 @@ impl Chat { div() .group("") + .w_full() .relative() .flex() .gap_3() - .w_full() - .p_2() + .px_3() + .py_2() .map(|this| match message { RoomMessage::User(item) => { let text = text_data @@ -355,6 +355,7 @@ impl Chat { .or_insert_with(|| RichText::new(item.content.to_owned(), &item.mentions)); this.hover(|this| this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE))) + .text_sm() .child( div() .absolute() @@ -379,19 +380,18 @@ impl Chat { .flex() .items_baseline() .gap_2() - .text_xs() .child( div().font_semibold().child(item.author.shared_name()), ) - .child(div().child(item.ago()).text_color( - cx.theme().base.step(cx, ColorScaleStep::ELEVEN), - )), + .child( + div() + .text_color( + cx.theme().base.step(cx, ColorScaleStep::NINE), + ) + .child(item.ago()), + ), ) - .child(div().text_sm().child(text.element( - "body".into(), - window, - cx, - ))), + .child(text.element("body".into(), window, cx)), ) } RoomMessage::System(content) => this @@ -407,7 +407,7 @@ impl Chat { .group_hover("", |this| this.bg(cx.theme().danger)), ) .child(img("brand/avatar.png").size_8().flex_shrink_0()) - .text_xs() + .text_sm() .text_color(cx.theme().danger) .child(content.clone()), RoomMessage::Announcement => this @@ -419,12 +419,12 @@ impl Chat { .justify_center() .text_center() .text_xs() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) + .text_color(cx.theme().base.step(cx, ColorScaleStep::NINE)) .line_height(relative(1.3)) .child( svg() .path("brand/coop.svg") - .size_8() + .size_10() .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), ) .child(ROOM_DESCRIPTION), @@ -444,7 +444,7 @@ impl Panel for Chat { div() .flex() .items_center() - .gap_1() + .gap_1p5() .child( div() .flex() @@ -459,7 +459,7 @@ impl Panel for Chat { .map(|(ix, facepill)| { div() .when(ix > 0, |div| div.ml_neg_1()) - .child(img(facepill).size_4()) + .child(img(facepill).size_5()) }), ), ) @@ -491,7 +491,7 @@ impl Render for Chat { .size_full() .child(list(self.list_state.clone()).flex_1()) .child( - div().flex_shrink_0().p_2().child( + div().flex_shrink_0().px_3().py_2().child( div() .flex() .flex_col() @@ -539,7 +539,6 @@ impl Render for Chat { .child( div() .w_full() - .h_9() .flex() .items_center() .gap_2() @@ -550,30 +549,10 @@ impl Render for Chat { .on_click(cx.listener(move |this, _, window, cx| { this.upload_media(window, cx); })) + .disabled(self.is_uploading) .loading(self.is_uploading), ) - .child( - div() - .flex_1() - .flex() - .items_center() - .bg(cx.theme().base.step(cx, ColorScaleStep::THREE)) - .rounded(px(cx.theme().radius)) - .pl_2() - .pr_1() - .child(self.input.clone()) - .child( - Button::new("send") - .ghost() - .xsmall() - .bold() - .rounded(ButtonRounded::Medium) - .label("SEND") - .on_click(cx.listener(|this, _, window, cx| { - this.send_message(window, cx) - })), - ), - ), + .child(self.input.clone()), ), ), ) diff --git a/crates/coop/src/views/sidebar/compose.rs b/crates/coop/src/views/compose.rs similarity index 81% rename from crates/coop/src/views/sidebar/compose.rs rename to crates/coop/src/views/compose.rs index 0ff9062..513cdc1 100644 --- a/crates/coop/src/views/sidebar/compose.rs +++ b/crates/coop/src/views/compose.rs @@ -7,9 +7,9 @@ use common::{profile::SharedProfile, random_name}; use global::get_client; use gpui::{ div, img, impl_internal_actions, prelude::FluentBuilder, px, relative, uniform_list, App, - AppContext, ClickEvent, Context, Div, Entity, FocusHandle, InteractiveElement, IntoElement, - ParentElement, Render, RenderOnce, SharedString, StatefulInteractiveElement, Styled, - Subscription, Task, TextAlign, Window, + AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement, + Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, TextAlign, + Window, }; use nostr_sdk::prelude::*; use serde::Deserialize; @@ -17,18 +17,18 @@ use smallvec::{smallvec, SmallVec}; use smol::Timer; use std::{ collections::{BTreeSet, HashSet}, - rc::Rc, time::Duration, }; use ui::{ - button::{Button, ButtonRounded}, + button::{Button, ButtonVariants}, input::{InputEvent, TextInput}, theme::{scale::ColorScaleStep, ActiveTheme}, - ContextModal, Icon, IconName, Sizable, Size, StyledExt, + ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, }; -const DESCRIPTION: &str = - "Start a conversation with someone using their npub or NIP-05 (like foo@bar.com)."; +pub fn init(window: &mut Window, cx: &mut App) -> Entity { + cx.new(|cx| Compose::new(window, cx)) +} #[derive(Clone, PartialEq, Eq, Deserialize)] struct SelectContact(PublicKey); @@ -58,7 +58,7 @@ impl Compose { let name = random_name(2); let mut input = TextInput::new(window, cx) .appearance(false) - .text_size(Size::XSmall); + .text_size(Size::Small); input.set_placeholder("Family... . (Optional)"); input.set_text(name, window, cx); @@ -193,18 +193,6 @@ impl Compose { .detach(); } - pub fn label(&self, _window: &Window, cx: &App) -> SharedString { - if self.selected.read(cx).len() > 1 { - "Create Group DM".into() - } else { - "Create DM".into() - } - } - - pub fn is_submitting(&self) -> bool { - self.is_submitting - } - fn add(&mut self, window: &mut Window, cx: &mut Context) { let client = get_client(); let content = self.user_input.read(cx).text().to_string(); @@ -336,6 +324,15 @@ impl Compose { impl Render for Compose { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + const DESCRIPTION: &str = + "Start a conversation with someone using their npub or NIP-05 (like foo@bar.com)."; + + let label: SharedString = if self.selected.read(cx).len() > 1 { + "Create Group DM".into() + } else { + "Create DM".into() + }; + div() .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_action_select)) @@ -344,15 +341,13 @@ impl Render for Compose { .gap_1() .child( div() - .px_2() - .text_xs() + .text_sm() .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) .child(DESCRIPTION), ) .when_some(self.error_message.read(cx).as_ref(), |this, msg| { this.child( div() - .px_2() .text_xs() .text_color(cx.theme().danger) .child(msg.clone()), @@ -362,13 +357,12 @@ impl Render for Compose { div().flex().flex_col().child( div() .h_10() - .px_2() .border_b_1() .border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE)) .flex() .items_center() .gap_1() - .child(div().text_xs().font_semibold().child("Title:")) + .child(div().pb_0p5().text_sm().font_semibold().child("Title:")) .child(self.title_input.clone()), ), ) @@ -377,25 +371,9 @@ impl Render for Compose { .flex() .flex_col() .gap_2() - .child(div().px_2().text_xs().font_semibold().child("To:")) - .child( - div() - .flex() - .items_center() - .gap_2() - .px_2() - .child( - Button::new("add_user_to_compose_btn") - .icon(IconName::Plus) - .small() - .rounded(ButtonRounded::Size(px(9999.))) - .loading(self.is_loading) - .on_click(cx.listener(|this, _, window, cx| { - this.add(window, cx); - })), - ) - .child(self.user_input.clone()), - ) + .mt_1() + .child(div().text_sm().font_semibold().child("To:")) + .child(self.user_input.clone()) .map(|this| { let contacts = self.contacts.read(cx).clone(); let view = cx.entity(); @@ -444,30 +422,35 @@ impl Render for Compose { div() .id(ix) .w_full() - .h_9() + .h_10() .px_2() .flex() .items_center() .justify_between() + .rounded(px(cx.theme().radius)) .child( div() .flex() .items_center() - .gap_2() - .text_xs() - .child(div().flex_shrink_0().child( - img(item.shared_avatar()).size_6(), - )) + .gap_3() + .text_sm() + .child( + img(item.shared_avatar()) + .size_7() + .flex_shrink_0(), + ) .child(item.shared_name()), ) .when(is_select, |this| { this.child( - Icon::new(IconName::CircleCheck) - .size_3() - .text_color(cx.theme().base.step( - cx, - ColorScaleStep::TWELVE, - )), + Icon::new(IconName::CheckCircleFill) + .small() + .text_color( + cx.theme().accent.step( + cx, + ColorScaleStep::NINE, + ), + ), ) }) .hover(|this| { @@ -490,71 +473,22 @@ impl Render for Compose { items }, ) - .min_h(px(250.)), + .pb_4() + .min_h(px(280.)), ) } }), ) - } -} - -type Handler = Rc; - -#[derive(IntoElement)] -pub struct ComposeButton { - base: Div, - label: SharedString, - handler: Handler, -} - -impl ComposeButton { - pub fn new(label: impl Into) -> Self { - Self { - base: div(), - label: label.into(), - handler: Rc::new(|_, _, _| {}), - } - } - - pub fn on_click( - mut self, - handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.handler = Rc::new(handler); - self - } -} - -impl RenderOnce for ComposeButton { - fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { - let handler = self.handler.clone(); - - self.base - .id("compose") - .flex() - .items_center() - .gap_2() - .px_1() - .h_7() - .text_xs() - .font_semibold() - .rounded(px(cx.theme().radius)) - .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) .child( - div() - .size_6() - .flex() - .items_center() - .justify_center() - .rounded_full() - .bg(cx.theme().accent.step(cx, ColorScaleStep::NINE)) - .child( - Icon::new(IconName::ComposeFill) - .small() - .text_color(cx.theme().base.darken(cx)), - ), + div().mt_2().child( + Button::new("create_dm_btn") + .label(label) + .primary() + .w_full() + .loading(self.is_submitting) + .disabled(self.is_submitting) + .on_click(cx.listener(|this, _, window, cx| this.compose(window, cx))), + ), ) - .child(self.label.clone()) - .on_click(move |ev, window, cx| handler(ev, window, cx)) } } diff --git a/crates/coop/src/views/contacts.rs b/crates/coop/src/views/contacts.rs index e0525ea..380a7aa 100644 --- a/crates/coop/src/views/contacts.rs +++ b/crates/coop/src/views/contacts.rs @@ -19,6 +19,8 @@ use ui::{ Sizable, }; +const MIN_HEIGHT: f32 = 280.; + pub fn init(window: &mut Window, cx: &mut App) -> Entity { Contacts::new(window, cx) } @@ -108,51 +110,56 @@ impl Focusable for Contacts { impl Render for Contacts { fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context) -> impl IntoElement { - div().size_full().pt_2().px_2().map(|this| { + let entity = cx.entity().clone(); + + div().map(|this| { if let Some(contacts) = self.contacts.clone() { this.child( uniform_list( - cx.entity().clone(), + entity, "contacts", contacts.len(), move |_, range, _window, cx| { - let mut items = Vec::new(); + let mut items = Vec::with_capacity(contacts.len()); for ix in range { - let item = contacts.get(ix).unwrap().clone(); - - items.push( - div() - .w_full() - .h_9() - .px_2() - .flex() - .items_center() - .justify_between() - .rounded(px(cx.theme().radius)) - .child( - div() - .flex() - .items_center() - .gap_2() - .text_xs() - .child( - div() - .flex_shrink_0() - .child(img(item.shared_avatar()).size_6()), - ) - .child(item.shared_name()), - ) - .hover(|this| { - this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)) - }), - ); + if let Some(item) = contacts.get(ix) { + items.push( + div() + .w_full() + .h_9() + .px_2() + .flex() + .items_center() + .justify_between() + .rounded(px(cx.theme().radius)) + .child( + div() + .flex() + .items_center() + .gap_2() + .text_xs() + .child( + div().flex_shrink_0().child( + img(item.shared_avatar()).size_6(), + ), + ) + .child(item.shared_name()), + ) + .hover(|this| { + this.bg(cx + .theme() + .base + .step(cx, ColorScaleStep::THREE)) + }), + ); + } } items }, ) - .h_full(), + .min_h(px(MIN_HEIGHT)), ) } else { this.flex() diff --git a/crates/coop/src/views/login.rs b/crates/coop/src/views/login.rs index 8a53435..8635816 100644 --- a/crates/coop/src/views/login.rs +++ b/crates/coop/src/views/login.rs @@ -17,13 +17,9 @@ use ui::{ notification::Notification, popup_menu::PopupMenu, theme::{scale::ColorScaleStep, ActiveTheme}, - ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, + ContextModal, Disableable, Sizable, Size, StyledExt, }; -use crate::chat_space::ChatSpace; - -use super::onboarding; - const INPUT_INVALID: &str = "You must provide a valid Private Key or Bunker."; pub fn init(window: &mut Window, cx: &mut App) -> Entity { @@ -60,12 +56,12 @@ impl Login { let key_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .placeholder("nsec... or bunker://...") }); let connect_relay = cx.new(|cx| { - let mut input = TextInput::new(window, cx).text_size(Size::XSmall).small(); + let mut input = TextInput::new(window, cx).text_size(Size::Small).small(); input.set_text("wss://relay.nsec.app", window, cx); input }); @@ -233,11 +229,6 @@ impl Login { self.is_logging_in = status; cx.notify(); } - - fn back(&self, window: &mut Window, cx: &mut Context) { - let panel = onboarding::init(window, cx); - ChatSpace::set_center_panel(panel, window, cx); - } } impl Panel for Login { @@ -299,14 +290,13 @@ impl Render for Login { .child( div() .text_center() - .text_lg() + .text_xl() .font_semibold() - .line_height(relative(1.2)) + .line_height(relative(1.3)) .child("Welcome Back!"), ) .child( div() - .text_sm() .text_color( cx.theme().base.step(cx, ColorScaleStep::ELEVEN), ) @@ -365,7 +355,6 @@ impl Render for Login { .text_center() .child( div() - .text_sm() .font_semibold() .line_height(relative(1.2)) .text_color( @@ -375,7 +364,7 @@ impl Render for Login { ) .child( div() - .text_xs() + .text_sm() .text_color( cx.theme().base.step(cx, ColorScaleStep::ELEVEN), ) @@ -424,17 +413,5 @@ impl Render for Login { ), ), ) - .child( - div().absolute().left_2().top_10().w_16().child( - Button::new("back") - .label("Back") - .icon(Icon::new(IconName::ArrowLeft)) - .ghost() - .small() - .on_click(cx.listener(move |this, _, window, cx| { - this.back(window, cx); - })), - ), - ) } } diff --git a/crates/coop/src/views/mod.rs b/crates/coop/src/views/mod.rs index 132356d..c3d6f3b 100644 --- a/crates/coop/src/views/mod.rs +++ b/crates/coop/src/views/mod.rs @@ -1,10 +1,10 @@ pub mod chat; +pub mod compose; pub mod contacts; pub mod login; pub mod new_account; pub mod onboarding; pub mod profile; pub mod relays; -pub mod settings; pub mod sidebar; pub mod welcome; diff --git a/crates/coop/src/views/new_account.rs b/crates/coop/src/views/new_account.rs index 9ccbe91..41d86ad 100644 --- a/crates/coop/src/views/new_account.rs +++ b/crates/coop/src/views/new_account.rs @@ -19,10 +19,6 @@ use ui::{ Disableable, Icon, IconName, Sizable, Size, StyledExt, }; -use crate::chat_space::ChatSpace; - -use super::onboarding; - const STEAM_ID_DESCRIPTION: &str = "Steam ID is used to get your currently playing game and update your status."; @@ -52,26 +48,26 @@ impl NewAccount { fn view(window: &mut Window, cx: &mut Context) -> Self { let name_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .placeholder("Alice") }); let avatar_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .small() .placeholder("https://example.com/avatar.jpg") }); let steam_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .placeholder("76561199810385277") }); let bio_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .multi_line() .placeholder("A short introduce about you.") }); @@ -188,11 +184,6 @@ impl NewAccount { self.is_uploading = status; cx.notify(); } - - fn back(&self, window: &mut Window, cx: &mut Context) { - let panel = onboarding::init(window, cx); - ChatSpace::set_center_panel(panel, window, cx); - } } impl Panel for NewAccount { @@ -238,13 +229,13 @@ impl Render for NewAccount { .flex_col() .items_center() .justify_center() - .gap_8() + .gap_10() .child( div() .text_center() .text_lg() .font_semibold() - .line_height(relative(1.2)) + .line_height(relative(1.3)) .child("Create New Account"), ) .child( @@ -295,7 +286,7 @@ impl Render for NewAccount { .flex() .flex_col() .gap_1() - .text_xs() + .text_sm() .child("Name *:") .child(self.name_input.clone()), ) @@ -304,7 +295,7 @@ impl Render for NewAccount { .flex() .flex_col() .gap_1() - .text_xs() + .text_sm() .child("Bio:") .child(self.bio_input.clone()), ) @@ -313,7 +304,7 @@ impl Render for NewAccount { .flex() .flex_col() .gap_1() - .text_xs() + .text_sm() .child("Steam ID:") .child(self.steam_input.clone()) .child( @@ -341,17 +332,5 @@ impl Render for NewAccount { })), ), ) - .child( - div().absolute().left_2().top_10().w_16().child( - Button::new("back") - .label("Back") - .icon(Icon::new(IconName::ArrowLeft)) - .ghost() - .small() - .on_click(cx.listener(move |this, _, window, cx| { - this.back(window, cx); - })), - ), - ) } } diff --git a/crates/coop/src/views/onboarding.rs b/crates/coop/src/views/onboarding.rs index 5365294..57d7c2a 100644 --- a/crates/coop/src/views/onboarding.rs +++ b/crates/coop/src/views/onboarding.rs @@ -16,7 +16,7 @@ use super::{login, new_account}; const LOGO_URL: &str = "brand/coop.svg"; const TITLE: &str = "Welcome to Coop!"; -const SUBTITLE: &str = "a Nostr client for secure communication."; +const SUBTITLE: &str = "Secure Communication on Nostr."; pub fn init(window: &mut Window, cx: &mut App) -> Entity { Onboarding::new(window, cx) @@ -97,7 +97,7 @@ impl Render for Onboarding { .flex_col() .items_center() .justify_center() - .gap_8() + .gap_10() .child( div() .flex() @@ -107,7 +107,7 @@ impl Render for Onboarding { .child( svg() .path(LOGO_URL) - .size_12() + .size_16() .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), ) .child( @@ -115,14 +115,13 @@ impl Render for Onboarding { .text_center() .child( div() - .text_lg() + .text_xl() .font_semibold() - .line_height(relative(1.2)) + .line_height(relative(1.3)) .child(TITLE), ) .child( div() - .text_sm() .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) .child(SUBTITLE), ), diff --git a/crates/coop/src/views/profile.rs b/crates/coop/src/views/profile.rs index c5c6e60..195b989 100644 --- a/crates/coop/src/views/profile.rs +++ b/crates/coop/src/views/profile.rs @@ -2,46 +2,38 @@ use async_utility::task::spawn; use common::nip96_upload; use global::{constants::IMAGE_SERVICE, get_client}; use gpui::{ - div, img, prelude::FluentBuilder, AnyElement, App, AppContext, Context, Entity, EventEmitter, - Flatten, FocusHandle, Focusable, IntoElement, ParentElement, PathPromptOptions, Render, - SharedString, Styled, Task, Window, + div, img, prelude::FluentBuilder, px, App, AppContext, Context, Entity, Flatten, IntoElement, + ParentElement, PathPromptOptions, Render, Styled, Task, Window, }; use nostr_sdk::prelude::*; use smol::fs; -use std::{str::FromStr, sync::Arc, time::Duration}; +use std::{str::FromStr, time::Duration}; use ui::{ button::{Button, ButtonVariants}, - dock_area::panel::{Panel, PanelEvent}, input::TextInput, - popup_menu::PopupMenu, - ContextModal, Disableable, Sizable, Size, + theme::{scale::ColorScaleStep, ActiveTheme}, + ContextModal, Disableable, IconName, Sizable, Size, }; -pub fn init(window: &mut Window, cx: &mut App) -> Arc> { - Arc::new(Profile::new(window, cx)) +pub fn init(window: &mut Window, cx: &mut App) -> Entity { + Profile::new(window, cx) } pub struct Profile { profile: Option, - // Form name_input: Entity, avatar_input: Entity, bio_input: Entity, website_input: Entity, is_loading: bool, is_submitting: bool, - // Panel - name: SharedString, - focus_handle: FocusHandle, } impl Profile { pub fn new(window: &mut Window, cx: &mut App) -> Entity { - let window_handle = window.window_handle(); - let name_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .placeholder("Alice") }); @@ -54,13 +46,13 @@ impl Profile { let website_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .placeholder("https://your-website.com") }); let bio_input = cx.new(|cx| { TextInput::new(window, cx) - .text_size(Size::XSmall) + .text_size(Size::Small) .multi_line() .placeholder("A short introduce about you.") }); @@ -74,8 +66,6 @@ impl Profile { profile: None, is_loading: false, is_submitting: false, - name: "Profile".into(), - focus_handle: cx.focus_handle(), }; let task: Task, Error>> = cx.background_spawn(async move { @@ -89,10 +79,10 @@ impl Profile { Ok(metadata) }); - cx.spawn(async move |this, cx| { + cx.spawn_in(window, async move |this, cx| { if let Ok(Some(metadata)) = task.await { - _ = cx.update_window(window_handle, |_, window, cx| { - _ = this.update(cx, |this: &mut Profile, cx| { + cx.update(|window, cx| { + this.update(cx, |this: &mut Profile, cx| { this.avatar_input.update(cx, |this, cx| { if let Some(avatar) = metadata.picture.as_ref() { this.set_text(avatar, window, cx); @@ -114,9 +104,12 @@ impl Profile { } }); this.profile = Some(metadata); + cx.notify(); - }); - }); + }) + .ok(); + }) + .ok(); } }) .detach(); @@ -127,7 +120,6 @@ impl Profile { 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, @@ -137,7 +129,7 @@ impl Profile { // Show loading spinner self.set_loading(true, cx); - cx.spawn(async move |this, cx| { + cx.spawn_in(window, async move |this, cx| { match Flatten::flatten(paths.await.map_err(|e| e.into())) { Ok(Some(mut paths)) => { let path = paths.pop().unwrap(); @@ -153,32 +145,33 @@ impl Profile { }); if let Ok(url) = rx.await { - cx.update_window(window_handle, |_, window, cx| { + cx.update(|window, cx| { // Stop loading spinner this.update(cx, |this, cx| { this.set_loading(false, cx); }) - .unwrap(); + .ok(); // Set avatar input avatar_input .update(cx, |this, cx| { this.set_text(url.to_string(), window, cx); }) - .unwrap(); + .ok(); }) - .unwrap(); + .ok(); } } } Ok(None) => { - // Stop loading spinner - if let Some(view) = this.upgrade() { - cx.update_entity(&view, |this, cx| { + cx.update(|_, cx| { + // Stop loading spinner + this.update(cx, |this, cx| { this.set_loading(false, cx); }) - .unwrap(); - } + .ok(); + }) + .ok(); } Err(_) => {} } @@ -186,11 +179,6 @@ impl Profile { .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); @@ -216,82 +204,57 @@ impl Profile { new_metadata = new_metadata.website(url); } - let window_handle = window.window_handle(); - - cx.spawn(async move |this, cx| { + let task: Task> = cx.background_spawn(async move { let client = get_client(); - let (tx, rx) = oneshot::channel::(); + _ = client.set_metadata(&new_metadata).await?; - cx.background_spawn(async move { - if let Ok(output) = client.set_metadata(&new_metadata).await { - _ = tx.send(output.val); - } - }) - .detach(); + Ok(()) + }); - if rx.await.is_ok() { - cx.update_window(window_handle, |_, window, cx| { + cx.spawn_in(window, async move |this, cx| { + if task.await.is_ok() { + cx.update(|window, cx| { this.update(cx, |this, cx| { this.set_submitting(false, cx); window.push_notification("Your profile has been updated successfully", cx); }) - .unwrap() + .ok(); }) - .unwrap(); + .ok(); } }) .detach(); } + fn set_loading(&mut self, status: bool, cx: &mut Context) { + self.is_loading = status; + cx.notify(); + } + fn set_submitting(&mut self, status: bool, cx: &mut Context) { self.is_submitting = status; cx.notify(); } } -impl Panel for Profile { - fn panel_id(&self) -> SharedString { - "ProfilePanel".into() - } - - fn title(&self, _cx: &App) -> AnyElement { - self.name.clone().into_any_element() - } - - fn popup_menu(&self, menu: PopupMenu, _cx: &App) -> PopupMenu { - menu.track_focus(&self.focus_handle) - } - - fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec