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