diff --git a/Cargo.lock b/Cargo.lock index 2a4092d..2a0ba5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ahash" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0453232ace82dee0dd0b4c87a59bd90f7b53b314f3e0f61fe2ee7c8a16482289" + [[package]] name = "ahash" version = "0.8.12" @@ -864,9 +870,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "jobserver", "libc", @@ -1083,7 +1089,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1203,6 +1209,7 @@ dependencies = [ "smallvec", "smol", "theme", + "title_bar", "tracing-subscriber", "ui", ] @@ -1436,9 +1443,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4735f265ba6a1188052ca32d461028a7d1125868be18e287e756019da7607b5" +checksum = "ec09e802f5081de6157da9a75701d6c713d8dc3ba52571fd4bd25f412644e8a6" dependencies = [ "ctor-proc-macro", "dtor", @@ -1446,9 +1453,9 @@ dependencies = [ [[package]] name = "ctor-proc-macro" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" [[package]] name = "data-encoding" @@ -1484,7 +1491,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "proc-macro2", "quote", @@ -1580,6 +1587,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "dlv-list" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "downcast-rs" version = "1.2.1" @@ -1630,9 +1646,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" @@ -1649,7 +1665,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.2", + "toml 0.9.4", "vswhom", "winreg", ] @@ -1824,6 +1840,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "file-locker" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c3e69656680c6c3d76750b46dfa64bf07626bd2130c540d6cf2d306ba595a8" +dependencies = [ + "nix 0.29.0", +] + [[package]] name = "filedescriptor" version = "0.8.3" @@ -1947,7 +1972,7 @@ checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.7", "slotmap", "tinyvec", "ttf-parser 0.20.0", @@ -1961,7 +1986,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.7", "slotmap", "tinyvec", "ttf-parser 0.25.1", @@ -2018,6 +2043,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "freedesktop_entry_parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" +dependencies = [ + "nom", + "thiserror 1.0.69", +] + [[package]] name = "freetype-sys" version = "0.20.1" @@ -2336,7 +2371,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2429,7 +2464,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2441,7 +2476,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "gpui", "tokio", @@ -2485,6 +2520,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash 0.4.8", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -2663,7 +2707,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "anyhow", "bytes", @@ -2672,6 +2716,7 @@ dependencies = [ "http", "http-body", "log", + "parking_lot", "serde", "serde_json", "url", @@ -2681,7 +2726,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3271,7 +3316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -3282,14 +3327,37 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ "bitflags 2.9.1", "libc", ] +[[package]] +name = "linicon" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee8c5653188a809616c97296180a0547a61dba205bcdcbdd261dbd022a25fd9" +dependencies = [ + "file-locker", + "freedesktop_entry_parser", + "linicon-theme", + "memmap2 0.5.10", + "thiserror 1.0.69", +] + +[[package]] +name = "linicon-theme" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f8240c33bb08c5d8b8cdea87b683b05e61037aa76ff26bef40672cc6ecbb80" +dependencies = [ + "freedesktop_entry_parser", + "rust-ini", +] + [[package]] name = "linkify" version = "0.10.0" @@ -3359,9 +3427,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" +checksum = "86ea4e65087ff52f3862caff188d489f1fab49a0cb09e01b2e3f1a617b10aaed" [[package]] name = "lru-slab" @@ -3459,7 +3527,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "anyhow", "bindgen 0.71.1", @@ -3478,6 +3546,15 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.7" @@ -3681,8 +3758,8 @@ dependencies = [ [[package]] name = "nostr" -version = "0.42.1" -source = "git+https://github.com/rust-nostr/nostr#dd8328ded8958c8c1133b293142da94c3e1d6f70" +version = "0.43.0" +source = "git+https://github.com/rust-nostr/nostr#9d91709c35f743361d68c16349d33979329ebd84" dependencies = [ "aes", "base64", @@ -3704,8 +3781,8 @@ dependencies = [ [[package]] name = "nostr-connect" -version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#dd8328ded8958c8c1133b293142da94c3e1d6f70" +version = "0.43.0" +source = "git+https://github.com/rust-nostr/nostr#9d91709c35f743361d68c16349d33979329ebd84" dependencies = [ "async-utility", "nostr", @@ -3716,8 +3793,8 @@ dependencies = [ [[package]] name = "nostr-database" -version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#dd8328ded8958c8c1133b293142da94c3e1d6f70" +version = "0.43.0" +source = "git+https://github.com/rust-nostr/nostr#9d91709c35f743361d68c16349d33979329ebd84" dependencies = [ "flatbuffers", "lru", @@ -3727,10 +3804,11 @@ dependencies = [ [[package]] name = "nostr-lmdb" -version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#dd8328ded8958c8c1133b293142da94c3e1d6f70" +version = "0.43.0" +source = "git+https://github.com/rust-nostr/nostr#9d91709c35f743361d68c16349d33979329ebd84" dependencies = [ "async-utility", + "flume", "heed", "nostr", "nostr-database", @@ -3740,8 +3818,8 @@ dependencies = [ [[package]] name = "nostr-relay-pool" -version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#dd8328ded8958c8c1133b293142da94c3e1d6f70" +version = "0.43.0" +source = "git+https://github.com/rust-nostr/nostr#9d91709c35f743361d68c16349d33979329ebd84" dependencies = [ "async-utility", "async-wsocket", @@ -3756,8 +3834,8 @@ dependencies = [ [[package]] name = "nostr-sdk" -version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#dd8328ded8958c8c1133b293142da94c3e1d6f70" +version = "0.43.0" +source = "git+https://github.com/rust-nostr/nostr#9d91709c35f743361d68c16349d33979329ebd84" dependencies = [ "async-utility", "nostr", @@ -4149,6 +4227,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-multimap" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +dependencies = [ + "dlv-list", + "hashbrown 0.9.1", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -4666,9 +4754,9 @@ dependencies = [ [[package]] name = "rangemap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" [[package]] name = "rav1e" @@ -4770,9 +4858,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.15" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags 2.9.1", ] @@ -4811,7 +4899,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "derive_refineable", "workspace-hack", @@ -4963,7 +5051,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "anyhow", "bytes", @@ -5112,10 +5200,20 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.25" +name = "rust-ini" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -5166,9 +5264,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.29" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "aws-lc-rs", "log", @@ -5489,7 +5587,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "anyhow", "serde", @@ -5544,9 +5642,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "indexmap", "itoa", @@ -5884,7 +5982,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "arrayvec", "log", @@ -6303,11 +6401,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "title_bar" +version = "1.0.0" +dependencies = [ + "anyhow", + "common", + "gpui", + "i18n", + "linicon", + "log", + "nostr-sdk", + "rust-i18n", + "smallvec", + "smol", + "theme", + "ui", + "windows 0.61.3", +] + [[package]] name = "tokio" -version = "1.46.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -6316,9 +6433,9 @@ dependencies = [ "mio", "pin-project-lite", "slab", - "socket2 0.5.10", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6407,9 +6524,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1" dependencies = [ "indexmap", "serde", @@ -6857,7 +6974,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#af0c909924d5b5b46432847fce2afb4bc8d78ee2" +source = "git+https://github.com/zed-industries/zed#4d79edc7533d6d0a8e29004c789bc6041a97993d" dependencies = [ "anyhow", "async-fs", @@ -7125,13 +7242,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.0.8", "scoped-tls", "smallvec", "wayland-sys", @@ -7139,23 +7256,23 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ "bitflags 2.9.1", - "rustix 0.38.44", + "rustix 1.0.8", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-cursor" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.0.8", "wayland-client", "xcursor", ] @@ -7187,9 +7304,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml 0.37.5", @@ -7198,9 +7315,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -7489,7 +7606,7 @@ checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -7581,7 +7698,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -7632,10 +7749,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7946,7 +8064,7 @@ name = "xim" version = "0.4.0" source = "git+https://github.com/XDeme1/xim-rs?rev=d50d461764c2213655cd9cf65a0ea94c70d3c4fd#d50d461764c2213655cd9cf65a0ea94c70d3c4fd" dependencies = [ - "ahash", + "ahash 0.8.12", "hashbrown 0.14.5", "log", "x11rb", @@ -7978,7 +8096,7 @@ checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "as-raw-xcb-connection", "libc", - "memmap2", + "memmap2 0.9.7", "xkeysym", ] @@ -8212,9 +8330,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" dependencies = [ "zune-core", ] diff --git a/assets/icons/caret-down.svg b/assets/icons/caret-down.svg index 8de2648..ca53e93 100644 --- a/assets/icons/caret-down.svg +++ b/assets/icons/caret-down.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/icons/plus.svg b/assets/icons/plus.svg index 8544f9b..8cf89a0 100644 --- a/assets/icons/plus.svg +++ b/assets/icons/plus.svg @@ -1,3 +1,3 @@ - + diff --git a/crates/common/src/nip96.rs b/crates/common/src/nip96.rs index 5b71b39..10d4e6f 100644 --- a/crates/common/src/nip96.rs +++ b/crates/common/src/nip96.rs @@ -72,7 +72,11 @@ pub async fn nip96_upload( let json: Value = res.json().await?; let config = nip96::ServerConfig::from_json(json.to_string())?; - let signer = client.signer().await?; + let signer = if client.has_signer().await { + client.signer().await? + } else { + Keys::generate().into_nostr_signer() + }; let url = upload(&signer, &config, file, None).await?; diff --git a/crates/coop/Cargo.toml b/crates/coop/Cargo.toml index efa3bcd..3534fca 100644 --- a/crates/coop/Cargo.toml +++ b/crates/coop/Cargo.toml @@ -11,6 +11,7 @@ path = "src/main.rs" [dependencies] assets = { path = "../assets" } ui = { path = "../ui" } +title_bar = { path = "../title_bar" } identity = { path = "../identity" } theme = { path = "../theme" } common = { path = "../common" } diff --git a/crates/coop/src/chatspace.rs b/crates/coop/src/chatspace.rs index 4f01af0..9eec6be 100644 --- a/crates/coop/src/chatspace.rs +++ b/crates/coop/src/chatspace.rs @@ -1,33 +1,39 @@ use std::sync::Arc; -use anyhow::Error; use client_keys::ClientKeys; -use global::constants::{DEFAULT_MODAL_WIDTH, DEFAULT_SIDEBAR_WIDTH}; -use global::nostr_client; +use common::display::DisplayProfile; +use global::constants::DEFAULT_SIDEBAR_WIDTH; use gpui::prelude::FluentBuilder; use gpui::{ - div, px, relative, Action, App, AppContext, Axis, Context, Entity, InteractiveElement, - IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, Window, + actions, div, px, rems, Action, App, AppContext, Axis, Context, Entity, InteractiveElement, + IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window, }; use i18n::t; use identity::Identity; use nostr_connect::prelude::*; +use nostr_sdk::prelude::*; use registry::{Registry, RoomEmitter}; use serde::Deserialize; +use settings::AppSettings; use smallvec::{smallvec, SmallVec}; use theme::{ActiveTheme, Theme, ThemeMode}; +use title_bar::TitleBar; use ui::actions::OpenProfile; +use ui::avatar::Avatar; use ui::button::{Button, ButtonVariants}; use ui::dock_area::dock::DockPlacement; use ui::dock_area::panel::PanelView; use ui::dock_area::{ClosePanel, DockArea, DockItem}; use ui::modal::ModalButtonProps; -use ui::{ContextModal, IconName, Root, Sizable, StyledExt, TitleBar}; +use ui::popup_menu::PopupMenuExt; +use ui::{h_flex, ContextModal, IconName, Root, Sizable, StyledExt}; +use crate::views::compose::compose_button; use crate::views::screening::Screening; use crate::views::user_profile::UserProfile; use crate::views::{ - chat, login, new_account, onboarding, preferences, sidebar, startup, user_profile, welcome, + backup_keys, chat, login, messaging_relays, new_account, onboarding, preferences, sidebar, + startup, user_profile, welcome, }; pub fn init(window: &mut Window, cx: &mut App) -> Entity { @@ -44,6 +50,8 @@ pub fn new_account(window: &mut Window, cx: &mut App) { ChatSpace::set_center_panel(panel, window, cx); } +actions!(user, [DarkMode, Settings, Logout]); + #[derive(Clone, PartialEq, Eq, Deserialize)] pub enum PanelKind { Room(u64), @@ -70,14 +78,15 @@ pub struct ToggleModal { } pub struct ChatSpace { + title_bar: Entity, dock: Entity, - toolbar: bool, #[allow(unused)] subscriptions: SmallVec<[Subscription; 5]>, } impl ChatSpace { pub fn new(window: &mut Window, cx: &mut App) -> Entity { + let title_bar = cx.new(|_| TitleBar::new()); let dock = cx.new(|cx| { let panel = Arc::new(startup::init(window, cx)); let center = DockItem::panel(panel); @@ -99,14 +108,17 @@ impl ChatSpace { window, |_this: &mut Self, state, window, cx| { if !state.read(cx).has_keys() { - window.open_modal(cx, |this, _window, cx| { + let title = SharedString::new(t!("startup.client_keys_warning")); + let desc = SharedString::new(t!("startup.client_keys_desc")); + + window.open_modal(cx, move |this, _window, cx| { this.overlay_closable(false) .show_close(false) .keyboard(false) .confirm() .button_props( ModalButtonProps::default() - .cancel_text(t!("chatspace.create_new_keys")) + .cancel_text(t!("startup.create_new_keys")) .ok_text(t!("common.allow")), ) .child( @@ -124,13 +136,9 @@ impl ChatSpace { div() .font_semibold() .text_color(cx.theme().text_muted) - .child(SharedString::new(t!("chatspace.warning"))), + .child(title.clone()), ) - .child(div().line_height(relative(1.4)).child( - SharedString::new(t!( - "chatspace.allow_keychain_access" - )), - )), + .child(desc.clone()), ) .on_cancel(|_, _window, cx| { ClientKeys::global(cx).update(cx, |this, cx| { @@ -157,21 +165,21 @@ impl ChatSpace { window, |this: &mut Self, state, window, cx| { if !state.read(cx).has_signer() { - this.open_onboarding(window, cx); + this.set_onboarding_panels(window, cx); } else { - this.open_chats(window, cx); + this.set_chat_panels(window, cx); } }, )); - // Automatically run on_load function when UserProfile is created + // Automatically run load function when UserProfile is created subscriptions.push(cx.observe_new::(|this, window, cx| { if let Some(window) = window { - this.on_load(window, cx); + this.load(window, cx); } })); - // Automatically run on_load function when Screening is created + // Automatically run load function when Screening is created subscriptions.push(cx.observe_new::(|this, window, cx| { if let Some(window) = window { this.load(window, cx); @@ -196,7 +204,7 @@ impl ChatSpace { this.add_panel(panel, DockPlacement::Center, window, cx); }); } else { - window.push_notification(t!("chatspace.failed_to_open_room"), cx); + window.push_notification(t!("common.room_error"), cx); } } RoomEmitter::Close(..) => { @@ -216,16 +224,13 @@ impl ChatSpace { Self { dock, + title_bar, subscriptions, - toolbar: false, } }) } - pub fn open_onboarding(&mut self, window: &mut Window, cx: &mut Context) { - // No active user, disable user's toolbar - self.toolbar(false, cx); - + pub fn set_onboarding_panels(&mut self, window: &mut Window, cx: &mut Context) { let panel = Arc::new(onboarding::init(window, cx)); let center = DockItem::panel(panel); @@ -235,17 +240,14 @@ impl ChatSpace { }); } - pub fn open_chats(&mut self, window: &mut Window, cx: &mut Context) { - // Enable the toolbar for logged in users - self.toolbar(true, cx); - - // Load all chat rooms from database - Registry::global(cx).update(cx, |this, cx| { - this.load_rooms(window, cx); - }); - + pub fn set_chat_panels(&mut self, window: &mut Window, cx: &mut Context) { + let registry = Registry::global(cx); let weak_dock = self.dock.downgrade(); + + // The left panel will render sidebar let left = DockItem::panel(Arc::new(sidebar::init(window, cx))); + + // The center panel will render chat rooms (as tabs) let center = DockItem::split_with_sizes( Axis::Vertical, vec![DockItem::tabs( @@ -261,66 +263,31 @@ impl ChatSpace { cx, ); + // Update dock self.dock.update(cx, |this, cx| { this.set_left_dock(left, Some(px(DEFAULT_SIDEBAR_WIDTH)), true, window, cx); this.set_center(center, window, cx); }); - cx.defer_in(window, |this, window, cx| { - let verify_messaging_relays = this.verify_messaging_relays(cx); - - cx.spawn_in(window, async move |_, cx| { - if let Ok(status) = verify_messaging_relays.await { - if !status { - cx.update(|window, cx| { - window.dispatch_action( - Box::new(ToggleModal { - modal: ModalKind::SetupRelay, - }), - cx, - ); - }) - .ok(); - } - } - }) - .detach(); + // Load all chat rooms from the database + registry.update(cx, |this, cx| { + this.load_rooms(window, cx); }); } - pub fn open_settings(&mut self, window: &mut Window, cx: &mut Context) { - let settings = preferences::init(window, cx); - let title = SharedString::new(t!("chatspace.preferences_title")); + pub fn on_settings(&mut self, _ev: &Settings, window: &mut Window, cx: &mut Context) { + let view = preferences::init(window, cx); + let title = SharedString::new(t!("common.preferences")); window.open_modal(cx, move |modal, _, _| { modal .title(title.clone()) - .width(px(DEFAULT_MODAL_WIDTH)) - .child(settings.clone()) + .width(px(480.)) + .child(view.clone()) }); } - fn toolbar(&mut self, status: bool, cx: &mut Context) { - self.toolbar = status; - cx.notify(); - } - - fn verify_messaging_relays(&self, cx: &App) -> Task> { - cx.background_spawn(async move { - let client = nostr_client(); - let signer = client.signer().await?; - let public_key = signer.get_public_key().await?; - let filter = Filter::new() - .kind(Kind::InboxRelays) - .author(public_key) - .limit(1); - let is_exist = client.database().query(filter).await?.first().is_some(); - - Ok(is_exist) - }) - } - - fn toggle_appearance(&self, window: &mut Window, cx: &mut App) { + fn on_dark_mode(&mut self, _ev: &DarkMode, window: &mut Window, cx: &mut Context) { if cx.theme().mode.is_dark() { Theme::change(ThemeMode::Light, Some(window), cx); } else { @@ -328,23 +295,80 @@ impl ChatSpace { } } - fn logout(&self, window: &mut Window, cx: &mut App) { - Identity::global(cx).update(cx, |this, cx| { + fn on_sign_out(&mut self, _ev: &Logout, window: &mut Window, cx: &mut Context) { + let identity = Identity::global(cx); + // TODO: save current session? + identity.update(cx, |this, cx| { this.unload(window, cx); }); } - fn on_open_profile(&mut self, a: &OpenProfile, window: &mut Window, cx: &mut Context) { - let public_key = a.0; + fn on_open_profile(&mut self, ev: &OpenProfile, window: &mut Window, cx: &mut Context) { + let public_key = ev.0; let profile = user_profile::init(public_key, window, cx); window.open_modal(cx, move |this, _window, _cx| { - // user_profile::init(public_key, window, cx) - this.child(profile.clone()) + this.alert() + .show_close(true) + .overlay_closable(true) + .child(profile.clone()) + .button_props(ModalButtonProps::default().ok_text(t!("profile.njump"))) + .on_ok(move |_, _window, cx| { + let Ok(bech32) = public_key.to_bech32(); + cx.open_url(&format!("https://njump.me/{bech32}")); + false + }) }); } - pub(crate) fn set_center_panel(panel: P, window: &mut Window, cx: &mut App) { + fn render_titlebar_left_side( + &mut self, + _window: &mut Window, + _cx: &Context, + ) -> impl IntoElement { + let compose_button = compose_button().into_any_element(); + + h_flex().gap_1().child(compose_button) + } + + fn render_titlebar_right_side( + &mut self, + profile: &Profile, + _window: &mut Window, + cx: &Context, + ) -> impl IntoElement { + let proxy = AppSettings::get_proxy_user_avatars(cx); + let need_backup = Identity::read_global(cx).need_backup(); + let relay_ready = Identity::read_global(cx).relay_ready(); + + h_flex() + .gap_1() + .when_some(relay_ready, |this, status| { + this.when(!status, |this| this.child(messaging_relays::relay_button())) + }) + .when_some(need_backup, |this, keys| { + this.child(backup_keys::backup_button(keys.to_owned())) + }) + .child( + Button::new("user") + .small() + .reverse() + .transparent() + .icon(IconName::CaretDown) + .child(Avatar::new(profile.avatar_url(proxy)).size(rems(1.49))) + .popup_menu(|this, _window, _cx| { + this.menu(t!("user.dark_mode"), Box::new(DarkMode)) + .menu(t!("user.settings"), Box::new(Settings)) + .separator() + .menu(t!("user.sign_out"), Box::new(Logout)) + }), + ) + } + + pub(crate) fn set_center_panel

(panel: P, window: &mut Window, cx: &mut App) + where + P: PanelView, + { if let Some(Some(root)) = window.root::() { if let Ok(chatspace) = root.read(cx).view().clone().downcast::() { let panel = Arc::new(panel); @@ -365,7 +389,27 @@ impl Render for ChatSpace { let modal_layer = Root::render_modal_layer(window, cx); let notification_layer = Root::render_notification_layer(window, cx); + // Only render titlebar element if user is logged in + if let Some(identity) = Identity::read_global(cx).public_key() { + let profile = Registry::read_global(cx).get_person(&identity, cx); + + let left_side = self + .render_titlebar_left_side(window, cx) + .into_any_element(); + + let right_side = self + .render_titlebar_right_side(&profile, window, cx) + .into_any_element(); + + self.title_bar.update(cx, |this, _cx| { + this.set_children(vec![left_side, right_side]); + }) + } + div() + .on_action(cx.listener(Self::on_settings)) + .on_action(cx.listener(Self::on_dark_mode)) + .on_action(cx.listener(Self::on_sign_out)) .on_action(cx.listener(Self::on_open_profile)) .relative() .size_full() @@ -375,58 +419,7 @@ impl Render for ChatSpace { .flex_col() .size_full() // Title Bar - .child( - TitleBar::new() - // Left side - .child(div()) - // Right side - .when(self.toolbar, |this| { - this.child( - div() - .flex() - .items_center() - .justify_end() - .gap_1p5() - .px_2() - .child( - Button::new("appearance") - .tooltip(t!("chatspace.appearance_tooltip")) - .small() - .ghost() - .map(|this| { - if cx.theme().mode.is_dark() { - this.icon(IconName::Sun) - } else { - this.icon(IconName::Moon) - } - }) - .on_click(cx.listener(|this, _, window, cx| { - this.toggle_appearance(window, cx); - })), - ) - .child( - Button::new("preferences") - .tooltip(t!("chatspace.preferences_tooltip")) - .small() - .ghost() - .icon(IconName::Settings) - .on_click(cx.listener(|this, _, window, cx| { - this.open_settings(window, cx); - })), - ) - .child( - Button::new("logout") - .tooltip(t!("common.logout")) - .small() - .ghost() - .icon(IconName::Logout) - .on_click(cx.listener(|this, _, window, cx| { - this.logout(window, cx); - })), - ), - ) - }), - ) + .child(self.title_bar.clone()) // Dock .child(self.dock.clone()), ) diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 9204ef1..2c75285 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -5,21 +5,16 @@ use std::time::Duration; use anyhow::{anyhow, Error}; use assets::Assets; use auto_update::AutoUpdater; -#[cfg(not(target_os = "linux"))] -use global::constants::APP_NAME; use global::constants::{ - ALL_MESSAGES_SUB_ID, APP_ID, APP_PUBKEY, BOOTSTRAP_RELAYS, METADATA_BATCH_LIMIT, + ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, APP_PUBKEY, BOOTSTRAP_RELAYS, METADATA_BATCH_LIMIT, METADATA_BATCH_TIMEOUT, NEW_MESSAGE_SUB_ID, SEARCH_RELAYS, }; use global::{nostr_client, NostrSignal}; use gpui::{ - actions, px, size, App, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, - WindowBounds, WindowKind, WindowOptions, + actions, point, px, size, App, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, + SharedString, TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, + WindowKind, WindowOptions, }; -#[cfg(not(target_os = "linux"))] -use gpui::{point, SharedString, TitlebarOptions}; -#[cfg(target_os = "linux")] -use gpui::{WindowBackgroundAppearance, WindowDecorations}; use identity::Identity; use nostr_sdk::prelude::*; use registry::Registry; @@ -138,7 +133,7 @@ fn main() { continue; } - let duration = smol::Timer::after(Duration::from_secs(75)); + let duration = smol::Timer::after(Duration::from_secs(30)); let recv = || async { // prevent inline format @@ -202,25 +197,22 @@ fn main() { items: vec![MenuItem::action("Quit", Quit)], }]); + // Set up the window bounds + let bounds = Bounds::centered(None, size(px(920.0), px(700.0)), cx); + // Set up the window options let opts = WindowOptions { - #[cfg(not(target_os = "linux"))] + window_background: WindowBackgroundAppearance::Opaque, + window_decorations: Some(WindowDecorations::Client), + window_bounds: Some(WindowBounds::Windowed(bounds)), + window_min_size: Some(size(px(800.0), px(600.0))), + kind: WindowKind::Normal, + app_id: Some(APP_ID.to_owned()), titlebar: Some(TitlebarOptions { title: Some(SharedString::new_static(APP_NAME)), traffic_light_position: Some(point(px(9.0), px(9.0))), appears_transparent: true, }), - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(920.0), px(700.0)), - cx, - ))), - #[cfg(target_os = "linux")] - window_background: WindowBackgroundAppearance::Transparent, - #[cfg(target_os = "linux")] - window_decorations: Some(WindowDecorations::Client), - kind: WindowKind::Normal, - app_id: Some(APP_ID.to_owned()), ..Default::default() }; diff --git a/crates/coop/src/views/backup_keys.rs b/crates/coop/src/views/backup_keys.rs new file mode 100644 index 0000000..a32d3ba --- /dev/null +++ b/crates/coop/src/views/backup_keys.rs @@ -0,0 +1,258 @@ +use std::fs; +use std::time::Duration; + +use dirs::document_dir; +use gpui::prelude::FluentBuilder; +use gpui::{ + div, AppContext, ClipboardItem, Context, Entity, Flatten, IntoElement, ParentElement, Render, + SharedString, Styled, Window, +}; +use i18n::{shared_t, t}; +use identity::Identity; +use nostr_sdk::prelude::*; +use theme::ActiveTheme; +use ui::button::{Button, ButtonRounded, ButtonVariants}; +use ui::input::{InputState, TextInput}; +use ui::modal::ModalButtonProps; +use ui::{divider, h_flex, v_flex, ContextModal, Disableable, IconName, Sizable}; + +pub fn backup_button(keys: Keys) -> impl IntoElement { + div().child( + Button::new("backup") + .icon(IconName::Info) + .label(t!("new_account.backup_label")) + .danger() + .xsmall() + .rounded(ButtonRounded::Full) + .on_click(move |_, window, cx| { + let title = SharedString::new(t!("new_account.backup_label")); + let keys = keys.clone(); + let view = cx.new(|cx| BackupKeys::new(&keys, window, cx)); + let weak_view = view.downgrade(); + + window.open_modal(cx, move |modal, _window, _cx| { + let weak_view = weak_view.clone(); + + modal + .confirm() + .title(title.clone()) + .child(view.clone()) + .button_props( + ModalButtonProps::default() + .cancel_text(t!("new_account.backup_skip")) + .ok_text(t!("new_account.backup_download")), + ) + .on_ok(move |_, window, cx| { + weak_view + .update(cx, |this, cx| { + this.download(window, cx); + }) + .ok(); + // true to close the modal + false + }) + }) + }), + ) +} + +pub struct BackupKeys { + password: Entity, + pubkey_input: Entity, + secret_input: Entity, + error: Option, + copied: bool, +} + +impl BackupKeys { + pub fn new(keys: &Keys, window: &mut Window, cx: &mut Context<'_, Self>) -> Self { + let Ok(npub) = keys.public_key.to_bech32(); + let Ok(nsec) = keys.secret_key().to_bech32(); + + let password = cx.new(|cx| InputState::new(window, cx).masked(true)); + + let pubkey_input = cx.new(|cx| { + InputState::new(window, cx) + .disabled(true) + .default_value(npub) + }); + + let secret_input = cx.new(|cx| { + InputState::new(window, cx) + .disabled(true) + .default_value(nsec) + }); + + Self { + password, + pubkey_input, + secret_input, + error: None, + copied: false, + } + } + + fn copy_secret(&mut self, window: &mut Window, cx: &mut Context) { + let item = ClipboardItem::new_string(self.secret_input.read(cx).value().to_string()); + cx.write_to_clipboard(item); + + self.set_copied(true, window, cx); + } + + fn set_copied(&mut self, status: bool, window: &mut Window, cx: &mut Context) { + self.copied = status; + cx.notify(); + + // Reset the copied state after a delay + if status { + cx.spawn_in(window, async move |this, cx| { + cx.background_executor().timer(Duration::from_secs(2)).await; + cx.update(|window, cx| { + this.update(cx, |this, cx| { + this.set_copied(false, window, cx); + }) + .ok(); + }) + .ok(); + }) + .detach(); + } + } + + fn set_error(&mut self, error: E, window: &mut Window, cx: &mut Context) + where + E: Into, + { + self.error = Some(error.into()); + cx.notify(); + + // Clear the error message after a delay + cx.spawn_in(window, async move |this, cx| { + cx.background_executor().timer(Duration::from_secs(2)).await; + cx.update(|_, cx| { + this.update(cx, |this, cx| { + this.error = None; + cx.notify(); + }) + .ok(); + }) + .ok(); + }) + .detach(); + } + + fn download(&mut self, window: &mut Window, cx: &mut Context) { + let document_dir = document_dir().expect("Failed to get document directory"); + let password = self.password.read(cx).value().to_string(); + + if password.is_empty() { + self.set_error(t!("login.password_is_required"), window, cx); + return; + }; + + let path = cx.prompt_for_new_path(&document_dir); + let nsec = self.secret_input.read(cx).value().to_string(); + + cx.spawn_in(window, async move |this, cx| { + match Flatten::flatten(path.await.map_err(|e| e.into())) { + Ok(Ok(Some(path))) => { + cx.update(|window, cx| { + match fs::write(&path, nsec) { + Ok(_) => { + Identity::global(cx).update(cx, |this, cx| { + this.clear_need_backup(password, cx); + }); + // Close the current modal + window.close_modal(cx); + } + Err(e) => { + this.update(cx, |this, cx| { + this.set_error(e.to_string(), window, cx); + }) + .ok(); + } + }; + }) + .ok(); + } + _ => { + log::error!("Failed to save backup keys"); + } + }; + }) + .detach(); + } +} + +impl Render for BackupKeys { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + v_flex() + .gap_3() + .text_sm() + .child( + div() + .text_color(cx.theme().text_muted) + .child(shared_t!("new_account.backup_description")), + ) + .child( + v_flex() + .gap_1() + .child(shared_t!("common.pubkey")) + .child(TextInput::new(&self.pubkey_input).small()) + .child( + div() + .text_xs() + .text_color(cx.theme().text_muted) + .child(shared_t!("new_account.backup_pubkey_note")), + ), + ) + .child(divider(cx)) + .child( + v_flex() + .gap_1() + .child(shared_t!("common.secret")) + .child( + h_flex() + .gap_1() + .child(TextInput::new(&self.secret_input).small()) + .child( + Button::new("copy") + .icon({ + if self.copied { + IconName::CheckCircleFill + } else { + IconName::Copy + } + }) + .ghost() + .disabled(self.copied) + .on_click(cx.listener(move |this, _e, window, cx| { + this.copy_secret(window, cx); + })), + ), + ) + .child( + div() + .text_xs() + .text_color(cx.theme().text_muted) + .child(shared_t!("new_account.backup_secret_note")), + ), + ) + .child(divider(cx)) + .child( + v_flex() + .gap_1() + .child(shared_t!("login.set_password")) + .child(TextInput::new(&self.password).small()) + .when_some(self.error.as_ref(), |this, error| { + this.child( + div() + .italic() + .text_xs() + .text_color(cx.theme().danger_foreground) + .child(error.clone()), + ) + }), + ) + } +} diff --git a/crates/coop/src/views/chat/mod.rs b/crates/coop/src/views/chat/mod.rs index 64585a5..4a7b727 100644 --- a/crates/coop/src/views/chat/mod.rs +++ b/crates/coop/src/views/chat/mod.rs @@ -33,6 +33,7 @@ use ui::button::{Button, ButtonVariants}; use ui::dock_area::panel::{Panel, PanelEvent}; use ui::emoji_picker::EmojiPicker; use ui::input::{InputEvent, InputState, TextInput}; +use ui::modal::ModalButtonProps; use ui::notification::Notification; use ui::popup_menu::PopupMenu; use ui::text::RichText; @@ -813,7 +814,7 @@ impl Panel for Chat { } fn toolbar_buttons(&self, _window: &Window, cx: &App) -> Vec