From 2c2aeb915eedd3c05e7fa840fad11cc4c215151e Mon Sep 17 00:00:00 2001 From: reya Date: Wed, 30 Apr 2025 13:10:18 +0700 Subject: [PATCH] feat: add option for toggle chat folders --- Cargo.lock | 217 ++++++++++-------------- assets/icons/filter-fill.svg | 3 + assets/icons/filter.svg | 3 + assets/icons/toggle-fill.svg | 3 + assets/icons/toggle.svg | 3 + crates/coop/src/views/sidebar/folder.rs | 25 +++ crates/coop/src/views/sidebar/mod.rs | 147 ++++++++++++---- crates/ui/src/icon.rs | 8 + crates/ui/src/styled.rs | 2 +- 9 files changed, 247 insertions(+), 164 deletions(-) create mode 100644 assets/icons/filter-fill.svg create mode 100644 assets/icons/filter.svg create mode 100644 assets/icons/toggle-fill.svg create mode 100644 assets/icons/toggle.svg diff --git a/Cargo.lock b/Cargo.lock index f265c2e..512449f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,14 +252,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand 2.3.0", "futures-lite 2.6.0", + "pin-project-lite", "slab", ] @@ -439,7 +440,7 @@ dependencies = [ "gpui", "log", "nostr-sdk", - "reqwest 0.12.15", + "reqwest 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", "smol", "tempfile", ] @@ -781,9 +782,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] @@ -979,9 +980,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1135,7 +1136,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1524,7 +1525,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "proc-macro2", "quote", @@ -2315,7 +2316,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2407,7 +2408,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "proc-macro2", "quote", @@ -2631,7 +2632,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "anyhow", "bytes", @@ -2648,7 +2649,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3140,9 +3141,9 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" +checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" dependencies = [ "arrayvec", "smallvec", @@ -3389,7 +3390,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "anyhow", "bindgen 0.71.1", @@ -3537,12 +3538,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "negentropy" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe" - [[package]] name = "negentropy" version = "0.5.0" @@ -3587,7 +3582,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr#2423323593f967a7c0d3136a1b0c3c89ee219d99" +source = "git+https://github.com/rust-nostr/nostr#7edeac7becbec534e89b0e3cc1aedf7f64691e2a" dependencies = [ "aes", "base64", @@ -3600,7 +3595,7 @@ dependencies = [ "getrandom 0.2.16", "instant", "regex", - "reqwest 0.12.15", + "reqwest 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", "scrypt", "secp256k1", "serde", @@ -3612,7 +3607,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr#2423323593f967a7c0d3136a1b0c3c89ee219d99" +source = "git+https://github.com/rust-nostr/nostr#7edeac7becbec534e89b0e3cc1aedf7f64691e2a" dependencies = [ "async-utility", "nostr", @@ -3624,7 +3619,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr#2423323593f967a7c0d3136a1b0c3c89ee219d99" +source = "git+https://github.com/rust-nostr/nostr#7edeac7becbec534e89b0e3cc1aedf7f64691e2a" dependencies = [ "flatbuffers", "lru", @@ -3635,7 +3630,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr#2423323593f967a7c0d3136a1b0c3c89ee219d99" +source = "git+https://github.com/rust-nostr/nostr#7edeac7becbec534e89b0e3cc1aedf7f64691e2a" dependencies = [ "async-utility", "heed", @@ -3648,14 +3643,13 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr#2423323593f967a7c0d3136a1b0c3c89ee219d99" +source = "git+https://github.com/rust-nostr/nostr#7edeac7becbec534e89b0e3cc1aedf7f64691e2a" dependencies = [ "async-utility", "async-wsocket", "atomic-destructor", "lru", - "negentropy 0.3.1", - "negentropy 0.5.0", + "negentropy", "nostr", "nostr-database", "tokio", @@ -3665,7 +3659,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr#2423323593f967a7c0d3136a1b0c3c89ee219d99" +source = "git+https://github.com/rust-nostr/nostr#7edeac7becbec534e89b0e3cc1aedf7f64691e2a" dependencies = [ "async-utility", "nostr", @@ -4822,7 +4816,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "derive_refineable", "workspace-hack", @@ -4857,53 +4851,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "reqwest" -version = "0.12.8" -source = "git+https://github.com/zed-industries/reqwest.git?rev=fd110f6998da16bbca97b6dddda9be7827c50e29#fd110f6998da16bbca97b6dddda9be7827c50e29" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-native-certs", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-rustls", - "tokio-socks", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "windows-registry 0.2.0", -] - [[package]] name = "reqwest" version = "0.12.15" @@ -4957,10 +4904,58 @@ dependencies = [ "windows-registry 0.4.0", ] +[[package]] +name = "reqwest" +version = "0.12.15" +source = "git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415#951c770a32f1998d6e999cef3e59e0013e6c4415" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry 0.4.0", +] + [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "anyhow", "bytes", @@ -4969,7 +4964,7 @@ dependencies = [ "http_client_tls", "log", "regex", - "reqwest 0.12.8", + "reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)", "serde", "smol", "tokio", @@ -5430,7 +5425,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "anyhow", "serde", @@ -5753,7 +5748,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "arrayvec", "log", @@ -6266,9 +6261,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900f6c86a685850b1bc9f6223b20125115ee3f31e01207d81655bbcc0aea9231" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -6287,9 +6282,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.25" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -6301,9 +6296,9 @@ dependencies = [ [[package]] name = "toml_write" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28391a4201ba7eb1984cfeb6862c0b3ea2cfe23332298967c749dddc0d6cd976" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" [[package]] name = "tower" @@ -6663,7 +6658,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f060918b578bb3b565adb9daf3a387d40496ca92" +source = "git+https://github.com/zed-industries/zed#1a4d7249f6cf59dc37f64c8f745f0dee47a4e9ce" dependencies = [ "anyhow", "async-fs", @@ -6921,9 +6916,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", "downcast-rs", @@ -6935,9 +6930,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ "bitflags 2.9.0", "rustix 0.38.44", @@ -6947,9 +6942,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ "rustix 0.38.44", "wayland-client", @@ -7244,17 +7239,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-registry" version = "0.4.0" @@ -7286,15 +7270,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.3.2" @@ -7304,16 +7279,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-strings" version = "0.3.1" diff --git a/assets/icons/filter-fill.svg b/assets/icons/filter-fill.svg new file mode 100644 index 0000000..97b94eb --- /dev/null +++ b/assets/icons/filter-fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/filter.svg b/assets/icons/filter.svg new file mode 100644 index 0000000..2b587d5 --- /dev/null +++ b/assets/icons/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/toggle-fill.svg b/assets/icons/toggle-fill.svg new file mode 100644 index 0000000..8b1e834 --- /dev/null +++ b/assets/icons/toggle-fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/toggle.svg b/assets/icons/toggle.svg new file mode 100644 index 0000000..e4bd9f9 --- /dev/null +++ b/assets/icons/toggle.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/coop/src/views/sidebar/folder.rs b/crates/coop/src/views/sidebar/folder.rs index e862c88..2906451 100644 --- a/crates/coop/src/views/sidebar/folder.rs +++ b/crates/coop/src/views/sidebar/folder.rs @@ -7,6 +7,7 @@ use gpui::{ }; use ui::{ theme::{scale::ColorScaleStep, ActiveTheme}, + tooltip::Tooltip, Collapsible, Icon, IconName, Sizable, StyledExt, }; @@ -16,6 +17,7 @@ type Handler = Rc; pub struct Parent { base: Div, icon: Option, + tooltip: Option, label: SharedString, items: Vec, collapsed: bool, @@ -28,6 +30,7 @@ impl Parent { base: div().flex().flex_col().gap_2(), label: label.into(), icon: None, + tooltip: None, items: Vec::new(), collapsed: false, handler: Rc::new(|_, _, _| {}), @@ -39,6 +42,11 @@ impl Parent { self } + pub fn tooltip(mut self, tooltip: impl Into) -> Self { + self.tooltip = Some(tooltip.into()); + self + } + pub fn collapsed(mut self, collapsed: bool) -> Self { self.collapsed = collapsed; self @@ -105,6 +113,11 @@ impl RenderOnce for Parent { .when_some(self.icon, |this, icon| this.child(icon.small())) .child(self.label.clone()), ) + .when_some(self.tooltip.clone(), |this, tooltip| { + this.tooltip(move |window, cx| { + Tooltip::new(tooltip.clone(), window, cx).into() + }) + }) .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) .on_click(move |ev, window, cx| handler(ev, window, cx)), ) @@ -118,6 +131,7 @@ impl RenderOnce for Parent { pub struct Folder { base: Div, icon: Option, + tooltip: Option, label: SharedString, items: Vec, collapsed: bool, @@ -130,6 +144,7 @@ impl Folder { base: div().flex().flex_col().gap_2(), label: label.into(), icon: None, + tooltip: None, items: Vec::new(), collapsed: false, handler: Rc::new(|_, _, _| {}), @@ -141,6 +156,11 @@ impl Folder { self } + pub fn tooltip(mut self, tooltip: impl Into) -> Self { + self.tooltip = Some(tooltip.into()); + self + } + pub fn collapsed(mut self, collapsed: bool) -> Self { self.collapsed = collapsed; self @@ -201,6 +221,11 @@ impl RenderOnce for Folder { .when_some(self.icon, |this, icon| this.child(icon.small())) .child(self.label.clone()), ) + .when_some(self.tooltip.clone(), |this, tooltip| { + this.tooltip(move |window, cx| { + Tooltip::new(tooltip.clone(), window, cx).into() + }) + }) .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) .on_click(move |ev, window, cx| handler(ev, window, cx)), ) diff --git a/crates/coop/src/views/sidebar/mod.rs b/crates/coop/src/views/sidebar/mod.rs index 86e8ffd..9e44347 100644 --- a/crates/coop/src/views/sidebar/mod.rs +++ b/crates/coop/src/views/sidebar/mod.rs @@ -1,3 +1,5 @@ +use std::{cmp::Reverse, collections::HashSet}; + use account::Account; use button::SidebarButton; use chats::{ @@ -10,10 +12,11 @@ use global::get_client; use gpui::{ actions, div, img, prelude::FluentBuilder, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, Render, - SharedString, Styled, Task, Window, + ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Task, Window, }; +use itertools::Itertools; use ui::{ - button::{Button, ButtonVariants}, + button::{Button, ButtonCustomVariant, ButtonVariants}, dock_area::{ dock::DockPlacement, panel::{Panel, PanelEvent}, @@ -36,13 +39,25 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity { Sidebar::new(window, cx) } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum Item { + Ongoing, + Incoming, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum SubItem { + Trusted, + Unknown, +} + pub struct Sidebar { name: SharedString, + split_into_folders: bool, + active_items: HashSet, + active_subitems: HashSet, focus_handle: FocusHandle, - ongoing: bool, - incoming: bool, - trusted: bool, - unknown: bool, + scroll_handle: ScrollHandle, } impl Sidebar { @@ -52,34 +67,41 @@ impl Sidebar { fn view(_window: &mut Window, cx: &mut Context) -> Self { let focus_handle = cx.focus_handle(); + let scroll_handle = ScrollHandle::default(); + + let mut active_items = HashSet::with_capacity(2); + active_items.insert(Item::Ongoing); + + let mut active_subitems = HashSet::with_capacity(2); + active_subitems.insert(SubItem::Trusted); + active_subitems.insert(SubItem::Unknown); Self { name: "Chat Sidebar".into(), - ongoing: false, - incoming: false, - trusted: true, - unknown: true, + split_into_folders: false, + active_items, + active_subitems, focus_handle, + scroll_handle, } } - fn ongoing(&mut self, cx: &mut Context) { - self.ongoing = !self.ongoing; + fn toggle_item(&mut self, item: Item, cx: &mut Context) { + if !self.active_items.remove(&item) { + self.active_items.insert(item); + } cx.notify(); } - fn incoming(&mut self, cx: &mut Context) { - self.incoming = !self.incoming; + fn toggle_subitem(&mut self, subitem: SubItem, cx: &mut Context) { + if !self.active_subitems.remove(&subitem) { + self.active_subitems.insert(subitem); + } cx.notify(); } - fn trusted(&mut self, cx: &mut Context) { - self.trusted = !self.trusted; - cx.notify(); - } - - fn unknown(&mut self, cx: &mut Context) { - self.unknown = !self.unknown; + fn split_into_folders(&mut self, cx: &mut Context) { + self.split_into_folders = !self.split_into_folders; cx.notify(); } @@ -176,20 +198,18 @@ impl Focusable for Sidebar { } impl Render for Sidebar { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let account = Account::global(cx).read(cx).profile.as_ref(); let registry = ChatRegistry::global(cx).read(cx); let rooms = registry.rooms(cx); let loading = registry.loading(); - let ongoing = rooms.get(&RoomKind::Ongoing); - let trusted = rooms.get(&RoomKind::Trusted); - let unknown = rooms.get(&RoomKind::Unknown); - div() - .scrollable(cx.entity_id(), ScrollbarAxis::Vertical) + .id("sidebar") + .track_scroll(&self.scroll_handle) .on_action(cx.listener(Self::on_logout)) + .overflow_y_scroll() .size_full() .flex() .flex_col() @@ -279,23 +299,65 @@ impl Render for Sidebar { .gap_2() .child( div() - .px_2() + .pl_2() + .pr_1() + .flex() + .justify_between() + .items_center() .text_xs() .font_semibold() .text_color(cx.theme().base.step(cx, ColorScaleStep::NINE)) - .child("Messages"), + .child("Messages") + .child( + Button::new("menu") + .tooltip("Toggle chat folders") + .map(|this| { + if self.split_into_folders { + this.icon(IconName::ToggleFill) + } else { + this.icon(IconName::Toggle) + } + }) + .small() + .custom( + ButtonCustomVariant::new(window, cx) + .foreground( + cx.theme().base.step(cx, ColorScaleStep::NINE), + ) + .color(cx.theme().transparent) + .hover(cx.theme().transparent) + .active(cx.theme().transparent) + .border(cx.theme().transparent), + ) + .on_click(cx.listener(move |this, _, _, cx| { + this.split_into_folders(cx); + })), + ), ) .map(|this| { if loading { this.children(self.render_skeleton(6)) + } else if !self.split_into_folders { + let rooms: Vec<_> = rooms + .values() + .flat_map(|v| v.iter().cloned()) + .sorted_by_key(|e| Reverse(e.read(cx).created_at)) + .collect(); + + this.children(Self::render_items(&rooms, cx)) } else { + let ongoing = rooms.get(&RoomKind::Ongoing); + let trusted = rooms.get(&RoomKind::Trusted); + let unknown = rooms.get(&RoomKind::Unknown); + this.when_some(ongoing, |this, rooms| { this.child( Folder::new("Ongoing") .icon(IconName::Folder) - .collapsed(self.ongoing) + .tooltip("All ongoing conversations") + .collapsed(!self.active_items.contains(&Item::Ongoing)) .on_click(cx.listener(move |this, _, _, cx| { - this.ongoing(cx); + this.toggle_item(Item::Ongoing, cx); })) .children(Self::render_items(rooms, cx)), ) @@ -303,17 +365,23 @@ impl Render for Sidebar { .child( Parent::new("Incoming") .icon(IconName::Folder) - .collapsed(self.incoming) + .tooltip("Incoming messages") + .collapsed(!self.active_items.contains(&Item::Incoming)) .on_click(cx.listener(move |this, _, _, cx| { - this.incoming(cx); + this.toggle_item(Item::Incoming, cx); })) .when_some(trusted, |this, rooms| { this.child( Folder::new("Trusted") .icon(IconName::Folder) - .collapsed(self.trusted) + .tooltip("Incoming messages from trusted contacts") + .collapsed( + !self + .active_subitems + .contains(&SubItem::Trusted), + ) .on_click(cx.listener(move |this, _, _, cx| { - this.trusted(cx); + this.toggle_subitem(SubItem::Trusted, cx); })) .children(Self::render_items(rooms, cx)), ) @@ -322,9 +390,14 @@ impl Render for Sidebar { this.child( Folder::new("Unknown") .icon(IconName::Folder) - .collapsed(self.unknown) + .tooltip("Incoming messages from unknowns") + .collapsed( + !self + .active_subitems + .contains(&SubItem::Unknown), + ) .on_click(cx.listener(move |this, _, _, cx| { - this.unknown(cx); + this.toggle_subitem(SubItem::Unknown, cx); })) .children(Self::render_items(rooms, cx)), ) diff --git a/crates/ui/src/icon.rs b/crates/ui/src/icon.rs index 3f631be..fd18ae7 100644 --- a/crates/ui/src/icon.rs +++ b/crates/ui/src/icon.rs @@ -36,6 +36,8 @@ pub enum IconName { EmojiFill, Folder, FolderFill, + Filter, + FilterFill, Inbox, Info, Loader, @@ -61,6 +63,8 @@ pub enum IconName { SortAscending, SortDescending, Sun, + Toggle, + ToggleFill, ThumbsDown, ThumbsUp, TriangleAlert, @@ -101,6 +105,8 @@ impl IconName { Self::EyeOff => "icons/eye-off.svg", Self::Folder => "icons/folder.svg", Self::FolderFill => "icons/folder-fill.svg", + Self::Filter => "icons/filter.svg", + Self::FilterFill => "icons/filter-fill.svg", Self::Inbox => "icons/inbox.svg", Self::Info => "icons/info.svg", Self::Loader => "icons/loader.svg", @@ -126,6 +132,8 @@ impl IconName { Self::SortAscending => "icons/sort-ascending.svg", Self::SortDescending => "icons/sort-descending.svg", Self::Sun => "icons/sun.svg", + Self::Toggle => "icons/toggle.svg", + Self::ToggleFill => "icons/toggle-fill.svg", Self::ThumbsDown => "icons/thumbs-down.svg", Self::ThumbsUp => "icons/thumbs-up.svg", Self::TriangleAlert => "icons/triangle-alert.svg", diff --git a/crates/ui/src/styled.rs b/crates/ui/src/styled.rs index a97e8de..2e40986 100644 --- a/crates/ui/src/styled.rs +++ b/crates/ui/src/styled.rs @@ -64,7 +64,7 @@ pub trait StyledExt: Styled + Sized { /// Set as Popover style fn popover_style(self, cx: &mut App) -> Self { - self.bg(cx.theme().base.step(cx, ColorScaleStep::TWO)) + self.bg(cx.theme().background) .border_1() .border_color(cx.theme().base.step(cx, ColorScaleStep::SIX)) .shadow_lg()