Compare commits
7 Commits
0.1.1-alph
...
0.1.2-alph
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e1d76bbcd | |||
| 61fb90bd34 | |||
| 50242981a5 | |||
| 85c485a4e4 | |||
| 48af00950a | |||
| 31e94c53c6 | |||
| ae01a2d67a |
470
Cargo.lock
generated
470
Cargo.lock
generated
@@ -49,7 +49,7 @@ dependencies = [
|
||||
"const-random",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -195,20 +195,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.10.2"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3"
|
||||
checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df"
|
||||
dependencies = [
|
||||
"async-fs",
|
||||
"async-net",
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand",
|
||||
"rand 0.9.0",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"url",
|
||||
"zbus 5.5.0",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -741,9 +741,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.6"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73848a43c5d63a1251d17adf6c2bf78aa94830e60a335a95eeea45d6ba9e1e4d"
|
||||
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
@@ -815,40 +815,6 @@ dependencies = [
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-packager-updater"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b60a6cdf300a0f0ee62f3f435e23cc97ef7a0a3a489a3aee6b0ca0cd986d722b"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"cargo-packager-utils",
|
||||
"ctor 0.2.9",
|
||||
"dirs 5.0.1",
|
||||
"dunce",
|
||||
"flate2",
|
||||
"http",
|
||||
"minisign-verify",
|
||||
"reqwest 0.12.12",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-packager-utils"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b43458dd2ee3cdab3f5b105acd80791383b730380c929018701313d7d299d4e8"
|
||||
dependencies = [
|
||||
"ctor 0.2.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
@@ -964,6 +930,7 @@ dependencies = [
|
||||
"gpui",
|
||||
"itertools 0.13.0",
|
||||
"nostr-sdk",
|
||||
"oneshot",
|
||||
"smol",
|
||||
"state",
|
||||
]
|
||||
@@ -1006,9 +973,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.29"
|
||||
version = "4.5.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||
checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -1016,9 +983,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.29"
|
||||
version = "4.5.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||
checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -1126,7 +1093,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collections"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"rustc-hash 2.1.1",
|
||||
@@ -1205,18 +1172,19 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "coop"
|
||||
version = "0.0.0"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo-packager-updater",
|
||||
"chats",
|
||||
"common",
|
||||
"dirs 5.0.1",
|
||||
"futures",
|
||||
"gpui",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"nostr-connect",
|
||||
"nostr-sdk",
|
||||
"oneshot",
|
||||
"reqwest_client",
|
||||
"rust-embed",
|
||||
"rustls",
|
||||
@@ -1224,7 +1192,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smol",
|
||||
"state",
|
||||
"tokio",
|
||||
"tracing-subscriber",
|
||||
"ui",
|
||||
]
|
||||
@@ -1402,25 +1369,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.2.9"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
||||
checksum = "cdd538fd2dbf2e5932fad5a4f983ff0458891f5ea40973fa2b7d3460ca378914"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"ctor-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.3.3"
|
||||
name = "ctor-proc-macro"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "952d7c24b2615675db90c2201742b49c314dea9fa56c6688cbf8c4dee2b2553f"
|
||||
checksum = "c426d2ba3e525b39c1f0a9ba41b9fe61878dee11fa4e4a76b6ab440f46c5db5d"
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
@@ -1434,15 +1400,6 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.19"
|
||||
@@ -1459,7 +1416,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1749,18 +1706,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flatbuffers"
|
||||
version = "23.5.26"
|
||||
@@ -2190,7 +2135,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"as-raw-xcb-connection",
|
||||
@@ -2212,7 +2157,7 @@ dependencies = [
|
||||
"core-graphics 0.23.2",
|
||||
"core-text",
|
||||
"cosmic-text",
|
||||
"ctor 0.3.3",
|
||||
"ctor",
|
||||
"derive_more",
|
||||
"embed-resource",
|
||||
"etagere",
|
||||
@@ -2240,7 +2185,7 @@ dependencies = [
|
||||
"pathfinder_geometry",
|
||||
"postage",
|
||||
"profiling",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"raw-window-handle",
|
||||
"refineable",
|
||||
"resvg",
|
||||
@@ -2277,7 +2222,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2292,9 +2237,9 @@ checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
|
||||
checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -2500,7 +2445,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -3037,7 +2982,6 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3188,12 +3132,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen 0.70.1",
|
||||
"core-foundation 0.9.4",
|
||||
"ctor 0.3.3",
|
||||
"ctor",
|
||||
"foreign-types",
|
||||
"metal",
|
||||
"objc",
|
||||
@@ -3260,12 +3204,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "minisign-verify"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
@@ -3373,7 +3311,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
[[package]]
|
||||
name = "nostr"
|
||||
version = "0.39.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr#43b9080a5b9f24466bd892f80c22ae4845d1fab1"
|
||||
source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64",
|
||||
@@ -3385,7 +3323,6 @@ dependencies = [
|
||||
"chacha20poly1305",
|
||||
"getrandom 0.2.15",
|
||||
"instant",
|
||||
"js-sys",
|
||||
"reqwest 0.12.12",
|
||||
"scrypt",
|
||||
"secp256k1",
|
||||
@@ -3393,15 +3330,12 @@ dependencies = [
|
||||
"serde_json",
|
||||
"unicode-normalization",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostr-connect"
|
||||
version = "0.39.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr#43b9080a5b9f24466bd892f80c22ae4845d1fab1"
|
||||
source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"nostr",
|
||||
@@ -3413,7 +3347,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-database"
|
||||
version = "0.39.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr#43b9080a5b9f24466bd892f80c22ae4845d1fab1"
|
||||
source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3"
|
||||
dependencies = [
|
||||
"flatbuffers",
|
||||
"lru",
|
||||
@@ -3424,7 +3358,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-lmdb"
|
||||
version = "0.39.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr#43b9080a5b9f24466bd892f80c22ae4845d1fab1"
|
||||
source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"heed",
|
||||
@@ -3437,7 +3371,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-relay-pool"
|
||||
version = "0.39.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr#43b9080a5b9f24466bd892f80c22ae4845d1fab1"
|
||||
source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"async-wsocket",
|
||||
@@ -3454,7 +3388,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-sdk"
|
||||
version = "0.39.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr#43b9080a5b9f24466bd892f80c22ae4845d1fab1"
|
||||
source = "git+https://github.com/rust-nostr/nostr#54dbd855c1143f77b5341fd522eb5bcedc5ed5c3"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"nostr",
|
||||
@@ -3511,7 +3445,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
@@ -3526,12 +3460,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
@@ -3820,16 +3748,22 @@ version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "oneshot"
|
||||
version = "0.1.10"
|
||||
source = "git+https://github.com/faern/oneshot#d36ef86c3cbcc54764391ae89805c160696cf57c"
|
||||
|
||||
[[package]]
|
||||
name = "oo7"
|
||||
version = "0.3.3"
|
||||
source = "git+https://github.com/zed-industries/oo7?branch=avoid-crypto-panic#9d5d5fcd7e4e0add9b420ffb58f67661b0b37568"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d939e731a8ef5d7809bedad43a7b4220d05093d5c76f7ee9c5289092bcb7bba4"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"ashpd",
|
||||
"async-fs",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-net",
|
||||
"blocking",
|
||||
"cbc",
|
||||
"cipher",
|
||||
@@ -3837,19 +3771,21 @@ dependencies = [
|
||||
"endi",
|
||||
"futures-lite 2.6.0",
|
||||
"futures-util",
|
||||
"getrandom 0.3.1",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"md-5",
|
||||
"num",
|
||||
"num-bigint-dig",
|
||||
"pbkdf2",
|
||||
"rand",
|
||||
"rand 0.9.0",
|
||||
"serde",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"zbus 4.4.0",
|
||||
"zbus",
|
||||
"zbus_macros",
|
||||
"zeroize",
|
||||
"zvariant 4.2.0",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3943,7 +3879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -4011,7 +3947,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4153,19 +4089,13 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4282,7 +4212,7 @@ checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.2.15",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"ring",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
@@ -4324,8 +4254,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.1",
|
||||
"zerocopy 0.8.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4335,7 +4276,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4347,6 +4298,16 @@ dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"zerocopy 0.8.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "random_name_generator"
|
||||
version = "0.3.6"
|
||||
@@ -4358,7 +4319,7 @@ dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rust-embed",
|
||||
"titlecase",
|
||||
@@ -4396,8 +4357,8 @@ dependencies = [
|
||||
"once_cell",
|
||||
"paste",
|
||||
"profiling",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"simd_helpers",
|
||||
"system-deps",
|
||||
"thiserror 1.0.69",
|
||||
@@ -4491,7 +4452,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"derive_refineable",
|
||||
]
|
||||
@@ -4580,7 +4541,6 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -4599,7 +4559,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
@@ -4609,13 +4568,11 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-socks",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"windows-registry",
|
||||
@@ -4624,13 +4581,14 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "reqwest_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"futures",
|
||||
"http_client",
|
||||
"log",
|
||||
"regex",
|
||||
"reqwest 0.12.8",
|
||||
"serde",
|
||||
"smol",
|
||||
@@ -4957,7 +4915,7 @@ version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"secp256k1-sys",
|
||||
"serde",
|
||||
]
|
||||
@@ -5003,7 +4961,7 @@ checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
|
||||
[[package]]
|
||||
name = "semantic_version"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -5212,9 +5170,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
|
||||
[[package]]
|
||||
name = "smol"
|
||||
@@ -5273,7 +5231,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"nostr-sdk",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5328,7 +5285,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
[[package]]
|
||||
name = "sum_tree"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
@@ -5553,17 +5510,6 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
@@ -5572,9 +5518,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
version = "3.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.3.0",
|
||||
@@ -5665,37 +5611,6 @@ dependencies = [
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@@ -5777,9 +5692,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -5820,9 +5733,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.26.1"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4bf6fecd69fcdede0ec680aaf474cdab988f9de6bc73d3758f0160e3b7025a"
|
||||
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
@@ -5979,17 +5892,16 @@ checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.26.1"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24"
|
||||
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"rand 0.9.0",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
@@ -6005,9 +5917,9 @@ checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "uds_windows"
|
||||
@@ -6198,7 +6110,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
[[package]]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#e60123bbdc58c37ad5ec60429fc730d5f9b8fd90"
|
||||
source = "git+https://github.com/zed-industries/zed#7a6b652ebcf9b7729b4ee69130bce0fbb529e6df"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-fs",
|
||||
@@ -6223,9 +6135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.13.1"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
|
||||
checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"serde",
|
||||
@@ -6989,17 +6901,6 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.8"
|
||||
@@ -7109,44 +7010,6 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
"async-fs",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-process",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"blocking",
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"nix",
|
||||
"ordered-stream",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"sha1",
|
||||
"static_assertions",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.52.0",
|
||||
"xdg-home",
|
||||
"zbus_macros 4.4.0",
|
||||
"zbus_names 3.0.0",
|
||||
"zvariant 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.5.0"
|
||||
@@ -7178,22 +7041,9 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"winnow",
|
||||
"xdg-home",
|
||||
"zbus_macros 5.5.0",
|
||||
"zbus_names 4.2.0",
|
||||
"zvariant 5.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"zvariant_utils 2.1.0",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7206,20 +7056,9 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"zbus_names 4.2.0",
|
||||
"zvariant 5.4.0",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant 4.2.0",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7231,7 +7070,7 @@ dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"winnow",
|
||||
"zvariant 5.4.0",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7247,7 +7086,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
"zerocopy-derive 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7261,6 +7109,17 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
@@ -7348,19 +7207,6 @@ dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant_derive 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.4.0"
|
||||
@@ -7373,21 +7219,8 @@ dependencies = [
|
||||
"static_assertions",
|
||||
"url",
|
||||
"winnow",
|
||||
"zvariant_derive 5.4.0",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"zvariant_utils 2.1.0",
|
||||
"zvariant_derive",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7400,18 +7233,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_utils"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -15,11 +15,15 @@ nostr-relay-builder = { git = "https://github.com/rust-nostr/nostr" }
|
||||
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
|
||||
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [
|
||||
"lmdb",
|
||||
"all-nips",
|
||||
"nip96",
|
||||
"nip59",
|
||||
"nip49",
|
||||
"nip44",
|
||||
"nip05",
|
||||
] }
|
||||
|
||||
smol = "2"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
oneshot = { git = "https://github.com/faern/oneshot" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
dirs = "5.0"
|
||||
@@ -30,6 +34,7 @@ tracing = "0.1.40"
|
||||
anyhow = "1.0.44"
|
||||
smallvec = "1.13.2"
|
||||
rust-embed = "8.5.0"
|
||||
log = "0.4"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
@@ -2,7 +2,7 @@ name = "coop"
|
||||
description = "Coop is a cross-platform Nostr client designed for secure communication focus on simplicity and customizability."
|
||||
product-name = "Coop"
|
||||
identifier = "su.reya.coop"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
resources = ["assets/*/*", "Cargo.toml", "./LICENSE", "./README.md"]
|
||||
icons = [
|
||||
"assets/brand/32x32.png",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "coop"
|
||||
version = "0.0.0"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
@@ -17,7 +17,6 @@ chats = { path = "../chats" }
|
||||
gpui.workspace = true
|
||||
reqwest_client.workspace = true
|
||||
|
||||
tokio.workspace = true
|
||||
nostr-connect.workspace = true
|
||||
nostr-sdk.workspace = true
|
||||
anyhow.workspace = true
|
||||
@@ -26,9 +25,10 @@ serde_json.workspace = true
|
||||
itertools.workspace = true
|
||||
dirs.workspace = true
|
||||
rust-embed.workspace = true
|
||||
log.workspace = true
|
||||
smol.workspace = true
|
||||
oneshot.workspace = true
|
||||
|
||||
rustls = "0.23.23"
|
||||
cargo-packager-updater = "0.2.2"
|
||||
futures= "0.3"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use asset::Assets;
|
||||
use async_utility::task::spawn;
|
||||
use chats::registry::ChatRegistry;
|
||||
use common::{
|
||||
constants::{
|
||||
ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, FAKE_SIG, KEYRING_SERVICE, NEW_MESSAGE_SUB_ID,
|
||||
},
|
||||
constants::{ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, KEYRING_SERVICE, NEW_MESSAGE_SUB_ID},
|
||||
profile::NostrProfile,
|
||||
};
|
||||
use futures::{select, FutureExt};
|
||||
use gpui::{
|
||||
actions, px, size, App, AppContext, Application, AsyncApp, Bounds, KeyBinding, Menu, MenuItem,
|
||||
WindowBounds, WindowKind, WindowOptions,
|
||||
@@ -16,20 +14,24 @@ use gpui::{point, SharedString, TitlebarOptions};
|
||||
#[cfg(target_os = "linux")]
|
||||
use gpui::{WindowBackgroundAppearance, WindowDecorations};
|
||||
use log::{error, info};
|
||||
use nostr_sdk::prelude::*;
|
||||
use nostr_sdk::{
|
||||
pool::prelude::ReqExitPolicy, Client, Event, Filter, Keys, Kind, Metadata, PublicKey,
|
||||
RelayMessage, RelayPoolNotification, SubscribeAutoCloseOptions,
|
||||
};
|
||||
use nostr_sdk::{prelude::NostrEventsDatabaseExt, FromBech32, SubscriptionId};
|
||||
use smol::Timer;
|
||||
use state::{get_client, initialize_client};
|
||||
use std::{borrow::Cow, collections::HashSet, str::FromStr, sync::Arc, time::Duration};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use std::{collections::HashSet, mem, sync::Arc, time::Duration};
|
||||
use ui::{theme::Theme, Root};
|
||||
use views::{app, onboarding, startup};
|
||||
|
||||
mod asset;
|
||||
mod views;
|
||||
|
||||
actions!(main_menu, [Quit]);
|
||||
actions!(coop, [Quit]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Signal {
|
||||
enum Signal {
|
||||
/// Receive event
|
||||
Event(Event),
|
||||
/// Receive EOSE
|
||||
@@ -37,214 +39,166 @@ pub enum Signal {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Issue: https://github.com/snapview/tokio-tungstenite/issues/353
|
||||
rustls::crypto::ring::default_provider()
|
||||
.install_default()
|
||||
.expect("Failed to install rustls crypto provider");
|
||||
// Fix crash on startup
|
||||
// TODO: why this is needed?
|
||||
_ = rustls::crypto::ring::default_provider().install_default();
|
||||
// Enable logging
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
// Initialize Nostr client
|
||||
initialize_client();
|
||||
let (event_tx, event_rx) = smol::channel::bounded::<Signal>(2048);
|
||||
let (batch_tx, batch_rx) = smol::channel::bounded::<Vec<PublicKey>>(100);
|
||||
|
||||
// Get client
|
||||
let client = get_client();
|
||||
let (signal_tx, mut signal_rx) = tokio::sync::mpsc::channel::<Signal>(2048);
|
||||
|
||||
spawn(async move {
|
||||
// Add some bootstrap relays
|
||||
_ = client.add_relay("wss://relay.damus.io/").await;
|
||||
_ = client.add_relay("wss://relay.primal.net/").await;
|
||||
_ = client.add_relay("wss://user.kindpag.es/").await;
|
||||
_ = client.add_relay("wss://directory.yabu.me/").await;
|
||||
_ = client.add_discovery_relay("wss://relaydiscovery.com").await;
|
||||
|
||||
// Connect to all relays
|
||||
_ = client.connect().await
|
||||
});
|
||||
|
||||
spawn(async move {
|
||||
let (batch_tx, mut batch_rx) = mpsc::channel::<Cow<Event>>(20);
|
||||
|
||||
async fn sync_metadata(client: &Client, buffer: &HashSet<PublicKey>) {
|
||||
let filter = Filter::new()
|
||||
.authors(buffer.iter().copied())
|
||||
.kind(Kind::Metadata)
|
||||
.limit(buffer.len());
|
||||
|
||||
if let Err(e) = client.sync(filter, &SyncOptions::default()).await {
|
||||
error!("NEG error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_batch(client: &Client, events: &[Cow<'_, Event>]) {
|
||||
let sig = Signature::from_str(FAKE_SIG).unwrap();
|
||||
let mut buffer: HashSet<PublicKey> = HashSet::with_capacity(20);
|
||||
|
||||
for event in events.iter() {
|
||||
if let Ok(UnwrappedGift { mut rumor, sender }) =
|
||||
client.unwrap_gift_wrap(event).await
|
||||
{
|
||||
let pubkeys: HashSet<PublicKey> = event.tags.public_keys().copied().collect();
|
||||
buffer.extend(pubkeys);
|
||||
buffer.insert(sender);
|
||||
|
||||
// Create event's ID is not exist
|
||||
rumor.ensure_id();
|
||||
|
||||
// Save event to database
|
||||
if let Some(id) = rumor.id {
|
||||
let ev = Event::new(
|
||||
id,
|
||||
rumor.pubkey,
|
||||
rumor.created_at,
|
||||
rumor.kind,
|
||||
rumor.tags,
|
||||
rumor.content,
|
||||
sig,
|
||||
);
|
||||
|
||||
if let Err(e) = client.database().save_event(&ev).await {
|
||||
error!("Save error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sync_metadata(client, &buffer).await;
|
||||
}
|
||||
|
||||
// Spawn a thread to handle batch process
|
||||
spawn(async move {
|
||||
const BATCH_SIZE: usize = 20;
|
||||
const BATCH_TIMEOUT: Duration = Duration::from_millis(200);
|
||||
|
||||
let mut batch = Vec::with_capacity(20);
|
||||
let mut timeout = Box::pin(tokio::time::sleep(BATCH_TIMEOUT));
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
event = batch_rx.recv() => {
|
||||
if let Some(event) = event {
|
||||
batch.push(event);
|
||||
|
||||
if batch.len() == BATCH_SIZE {
|
||||
process_batch(client, &batch).await;
|
||||
batch.clear();
|
||||
timeout = Box::pin(tokio::time::sleep(BATCH_TIMEOUT));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ = &mut timeout => {
|
||||
if !batch.is_empty() {
|
||||
process_batch(client, &batch).await;
|
||||
batch.clear();
|
||||
}
|
||||
timeout = Box::pin(tokio::time::sleep(BATCH_TIMEOUT));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let all_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
|
||||
let new_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
|
||||
let sig = Signature::from_str(FAKE_SIG).unwrap();
|
||||
let mut notifications = client.notifications();
|
||||
|
||||
while let Ok(notification) = notifications.recv().await {
|
||||
if let RelayPoolNotification::Message { message, .. } = notification {
|
||||
match message {
|
||||
RelayMessage::Event {
|
||||
event,
|
||||
subscription_id,
|
||||
} => match event.kind {
|
||||
Kind::GiftWrap => {
|
||||
if new_id == *subscription_id {
|
||||
if let Ok(UnwrappedGift { mut rumor, .. }) =
|
||||
client.unwrap_gift_wrap(&event).await
|
||||
{
|
||||
// Compute event id if not exist
|
||||
rumor.ensure_id();
|
||||
|
||||
if let Some(id) = rumor.id {
|
||||
let ev = Event::new(
|
||||
id,
|
||||
rumor.pubkey,
|
||||
rumor.created_at,
|
||||
rumor.kind,
|
||||
rumor.tags,
|
||||
rumor.content,
|
||||
sig,
|
||||
);
|
||||
|
||||
// Save rumor to database to further query
|
||||
if let Err(e) = client.database().save_event(&ev).await {
|
||||
error!("Save error: {}", e);
|
||||
}
|
||||
|
||||
// Send new event to GPUI
|
||||
if let Err(e) = signal_tx.send(Signal::Event(ev)).await {
|
||||
error!("Send error: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = batch_tx.send(event).await {
|
||||
error!("Failed to add to batch: {}", e);
|
||||
}
|
||||
}
|
||||
Kind::ContactList => {
|
||||
let public_keys: HashSet<_> =
|
||||
event.tags.public_keys().copied().collect();
|
||||
|
||||
sync_metadata(client, &public_keys).await;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
RelayMessage::EndOfStoredEvents(subscription_id) => {
|
||||
if all_id == *subscription_id {
|
||||
if let Err(e) = signal_tx.send(Signal::Eose).await {
|
||||
error!("Failed to send eose: {}", e)
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Initialize nostr client
|
||||
let client = initialize_client();
|
||||
|
||||
// Initialize application
|
||||
let app = Application::new()
|
||||
.with_assets(Assets)
|
||||
.with_http_client(Arc::new(reqwest_client::ReqwestClient::new()));
|
||||
|
||||
// Connect to default relays
|
||||
app.background_executor()
|
||||
.spawn(async {
|
||||
_ = client.add_relay("wss://relay.damus.io/").await;
|
||||
_ = client.add_relay("wss://relay.primal.net/").await;
|
||||
_ = client.add_relay("wss://user.kindpag.es/").await;
|
||||
_ = client.add_relay("wss://purplepag.es/").await;
|
||||
_ = client.add_discovery_relay("wss://relaydiscovery.com").await;
|
||||
_ = client.connect().await
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Handle batch metadata
|
||||
app.background_executor()
|
||||
.spawn(async move {
|
||||
const BATCH_SIZE: usize = 20;
|
||||
const BATCH_TIMEOUT: Duration = Duration::from_millis(200);
|
||||
|
||||
let mut batch: HashSet<PublicKey> = HashSet::new();
|
||||
|
||||
loop {
|
||||
let mut timeout = Box::pin(Timer::after(BATCH_TIMEOUT).fuse());
|
||||
|
||||
select! {
|
||||
pubkeys = batch_rx.recv().fuse() => {
|
||||
match pubkeys {
|
||||
Ok(keys) => {
|
||||
batch.extend(keys);
|
||||
if batch.len() >= BATCH_SIZE {
|
||||
sync_metadata(client, mem::take(&mut batch)).await;
|
||||
}
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
_ = timeout => {
|
||||
if !batch.is_empty() {
|
||||
sync_metadata(client, mem::take(&mut batch)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Handle notifications
|
||||
app.background_executor()
|
||||
.spawn(async move {
|
||||
let rng_keys = Keys::generate();
|
||||
let all_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
|
||||
let new_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
|
||||
let mut notifications = client.notifications();
|
||||
|
||||
while let Ok(notification) = notifications.recv().await {
|
||||
if let RelayPoolNotification::Message { message, .. } = notification {
|
||||
match message {
|
||||
RelayMessage::Event {
|
||||
event,
|
||||
subscription_id,
|
||||
} => {
|
||||
match event.kind {
|
||||
Kind::GiftWrap => {
|
||||
if let Ok(gift) = client.unwrap_gift_wrap(&event).await {
|
||||
let mut pubkeys = vec![];
|
||||
|
||||
// Sign the rumor with the generated keys,
|
||||
// this event will be used for internal only,
|
||||
// and NEVER send to relays.
|
||||
if let Ok(event) = gift.rumor.sign_with_keys(&rng_keys) {
|
||||
pubkeys.extend(event.tags.public_keys());
|
||||
pubkeys.push(event.pubkey);
|
||||
|
||||
// Save the event to the database, use for query directly.
|
||||
if let Err(e) =
|
||||
client.database().save_event(&event).await
|
||||
{
|
||||
error!("Failed to save event: {}", e);
|
||||
}
|
||||
|
||||
// Send all pubkeys to the batch
|
||||
if let Err(e) = batch_tx.send(pubkeys).await {
|
||||
error!("Failed to send pubkeys to batch: {}", e)
|
||||
}
|
||||
|
||||
// Send this event to the GPUI
|
||||
if new_id == *subscription_id {
|
||||
if let Err(e) =
|
||||
event_tx.send(Signal::Event(event)).await
|
||||
{
|
||||
error!("Failed to send event to GPUI: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::ContactList => {
|
||||
let pubkeys =
|
||||
event.tags.public_keys().copied().collect::<HashSet<_>>();
|
||||
sync_metadata(client, pubkeys).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
RelayMessage::EndOfStoredEvents(subscription_id) => {
|
||||
if all_id == *subscription_id {
|
||||
if let Err(e) = event_tx.send(Signal::Eose).await {
|
||||
error!("Failed to send eose: {}", e)
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Handle re-open window
|
||||
app.on_reopen(move |cx| {
|
||||
let client = get_client();
|
||||
let (tx, rx) = oneshot::channel::<Option<NostrProfile>>();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
cx.background_spawn(async move {
|
||||
if let Ok(signer) = client.signer().await {
|
||||
if let Ok(public_key) = signer.get_public_key().await {
|
||||
let metadata = if let Ok(Some(metadata)) =
|
||||
client.database().metadata(public_key).await
|
||||
{
|
||||
cx.background_spawn(async move {
|
||||
if let Ok(signer) = client.signer().await {
|
||||
if let Ok(public_key) = signer.get_public_key().await {
|
||||
let metadata =
|
||||
if let Ok(Some(metadata)) = client.database().metadata(public_key).await {
|
||||
metadata
|
||||
} else {
|
||||
Metadata::new()
|
||||
};
|
||||
|
||||
_ = tx.send(Some(NostrProfile::new(public_key, metadata)));
|
||||
} else {
|
||||
_ = tx.send(None);
|
||||
}
|
||||
_ = tx.send(Some(NostrProfile::new(public_key, metadata)));
|
||||
} else {
|
||||
_ = tx.send(None);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
} else {
|
||||
_ = tx.send(None);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Ok(result) = rx.await {
|
||||
_ = restore_window(result, &mut cx).await;
|
||||
}
|
||||
@@ -269,116 +223,131 @@ fn main() {
|
||||
items: vec![MenuItem::action("Quit", Quit)],
|
||||
}]);
|
||||
|
||||
let opts = WindowOptions {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
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(900.0), px(680.0)),
|
||||
cx,
|
||||
))),
|
||||
#[cfg(target_os = "linux")]
|
||||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
#[cfg(target_os = "linux")]
|
||||
window_decorations: Some(WindowDecorations::Client),
|
||||
kind: WindowKind::Normal,
|
||||
..Default::default()
|
||||
};
|
||||
// Open window with default options
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
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(900.0), px(680.0)),
|
||||
cx,
|
||||
))),
|
||||
#[cfg(target_os = "linux")]
|
||||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
#[cfg(target_os = "linux")]
|
||||
window_decorations: Some(WindowDecorations::Client),
|
||||
kind: WindowKind::Normal,
|
||||
..Default::default()
|
||||
},
|
||||
|window, cx| {
|
||||
window.set_window_title(APP_NAME);
|
||||
window.set_app_id(APP_ID);
|
||||
|
||||
cx.open_window(opts, |window, cx| {
|
||||
window.set_window_title(APP_NAME);
|
||||
window.set_app_id(APP_ID);
|
||||
window
|
||||
.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
})
|
||||
.detach();
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
window
|
||||
.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
})
|
||||
.detach();
|
||||
|
||||
let handle = window.window_handle();
|
||||
let root = cx.new(|cx| Root::new(startup::init(window, cx).into(), window, cx));
|
||||
let handle = window.window_handle();
|
||||
let root = cx.new(|cx| Root::new(startup::init(window, cx).into(), window, cx));
|
||||
|
||||
let task = cx.read_credentials(KEYRING_SERVICE);
|
||||
let (tx, rx) = oneshot::channel::<Option<NostrProfile>>();
|
||||
let task = cx.read_credentials(KEYRING_SERVICE);
|
||||
let (tx, rx) = oneshot::channel::<Option<NostrProfile>>();
|
||||
|
||||
// Read credential in OS Keyring
|
||||
cx.background_spawn(async {
|
||||
let profile = if let Ok(Some((npub, secret))) = task.await {
|
||||
let public_key = PublicKey::from_bech32(&npub).unwrap();
|
||||
let secret_hex = String::from_utf8(secret).unwrap();
|
||||
let keys = Keys::parse(&secret_hex).unwrap();
|
||||
// Read credential in OS Keyring
|
||||
cx.background_spawn(async {
|
||||
let profile = if let Ok(Some((npub, secret))) = task.await {
|
||||
let public_key = PublicKey::from_bech32(&npub).unwrap();
|
||||
let secret_hex = String::from_utf8(secret).unwrap();
|
||||
let keys = Keys::parse(&secret_hex).unwrap();
|
||||
|
||||
// Update nostr signer
|
||||
_ = client.set_signer(keys).await;
|
||||
// Update nostr signer
|
||||
_ = client.set_signer(keys).await;
|
||||
|
||||
// Get user's metadata
|
||||
let metadata =
|
||||
if let Ok(Some(metadata)) = client.database().metadata(public_key).await {
|
||||
// Get user's metadata
|
||||
let metadata = if let Ok(Some(metadata)) =
|
||||
client.database().metadata(public_key).await
|
||||
{
|
||||
metadata
|
||||
} else {
|
||||
Metadata::new()
|
||||
};
|
||||
|
||||
Some(NostrProfile::new(public_key, metadata))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(NostrProfile::new(public_key, metadata))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
_ = tx.send(profile)
|
||||
})
|
||||
.detach();
|
||||
_ = tx.send(profile)
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Set root view based on credential status
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Ok(Some(profile)) = rx.await {
|
||||
_ = cx.update_window(handle, |_, window, cx| {
|
||||
window.replace_root(cx, |window, cx| {
|
||||
Root::new(app::init(profile, window, cx).into(), window, cx)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_ = cx.update_window(handle, |_, window, cx| {
|
||||
window.replace_root(cx, |window, cx| {
|
||||
Root::new(onboarding::init(window, cx).into(), window, cx)
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Listen for messages from the Nostr thread
|
||||
cx.spawn(|cx| async move {
|
||||
while let Some(signal) = signal_rx.recv().await {
|
||||
match signal {
|
||||
Signal::Eose => {
|
||||
_ = cx.update(|cx| {
|
||||
if let Some(chats) = ChatRegistry::global(cx) {
|
||||
chats.update(cx, |this, cx| this.load_chat_rooms(cx))
|
||||
}
|
||||
// Set root view based on credential status
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Ok(Some(profile)) = rx.await {
|
||||
_ = cx.update_window(handle, |_, window, cx| {
|
||||
window.replace_root(cx, |window, cx| {
|
||||
Root::new(app::init(profile, window, cx).into(), window, cx)
|
||||
});
|
||||
}
|
||||
Signal::Event(event) => {
|
||||
_ = cx.update(|cx| {
|
||||
if let Some(chats) = ChatRegistry::global(cx) {
|
||||
chats.update(cx, |this, cx| this.push_message(event, cx))
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_ = cx.update_window(handle, |_, window, cx| {
|
||||
window.replace_root(cx, |window, cx| {
|
||||
Root::new(onboarding::init(window, cx).into(), window, cx)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
.detach();
|
||||
|
||||
root
|
||||
})
|
||||
cx.spawn(|cx| async move {
|
||||
while let Ok(signal) = event_rx.recv().await {
|
||||
cx.update(|cx| {
|
||||
match signal {
|
||||
Signal::Eose => {
|
||||
if let Some(chats) = ChatRegistry::global(cx) {
|
||||
chats.update(cx, |this, cx| this.load_chat_rooms(cx))
|
||||
}
|
||||
}
|
||||
Signal::Event(event) => {
|
||||
if let Some(chats) = ChatRegistry::global(cx) {
|
||||
chats.update(cx, |this, cx| this.push_message(event, cx))
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
root
|
||||
},
|
||||
)
|
||||
.expect("System error. Please re-open the app.");
|
||||
});
|
||||
}
|
||||
|
||||
async fn restore_window(profile: Option<NostrProfile>, cx: &mut AsyncApp) -> Result<()> {
|
||||
async fn sync_metadata(client: &Client, buffer: HashSet<PublicKey>) {
|
||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
let filter = Filter::new()
|
||||
.authors(buffer.iter().cloned())
|
||||
.kind(Kind::Metadata)
|
||||
.limit(buffer.len());
|
||||
|
||||
if let Err(e) = client.subscribe(filter, Some(opts)).await {
|
||||
error!("Subscribe error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn restore_window(profile: Option<NostrProfile>, cx: &mut AsyncApp) -> anyhow::Result<()> {
|
||||
let opts = cx
|
||||
.update(|cx| WindowOptions {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
@@ -405,6 +374,8 @@ async fn restore_window(profile: Option<NostrProfile>, cx: &mut AsyncApp) -> Res
|
||||
_ = cx.open_window(opts, |window, cx| {
|
||||
window.set_window_title(APP_NAME);
|
||||
window.set_app_id(APP_ID);
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
window
|
||||
.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
use cargo_packager_updater::{check_update, semver::Version, url::Url};
|
||||
use common::{
|
||||
constants::{UPDATER_PUBKEY, UPDATER_URL},
|
||||
profile::NostrProfile,
|
||||
};
|
||||
use common::profile::NostrProfile;
|
||||
use gpui::{
|
||||
actions, div, img, impl_internal_actions, prelude::FluentBuilder, px, App, AppContext, Axis,
|
||||
Context, Entity, InteractiveElement, IntoElement, ObjectFit, ParentElement, Render, Styled,
|
||||
StyledImage, Window,
|
||||
};
|
||||
use log::info;
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use state::get_client;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::{Button, ButtonRounded, ButtonVariants},
|
||||
dock_area::{dock::DockPlacement, DockArea, DockItem},
|
||||
@@ -88,26 +82,6 @@ impl AppView {
|
||||
view.set_center(center_panel, window, cx);
|
||||
});
|
||||
|
||||
// Check and auto update to the latest version
|
||||
cx.background_spawn(async move {
|
||||
// Set auto updater config
|
||||
let config = cargo_packager_updater::Config {
|
||||
endpoints: vec![Url::parse(UPDATER_URL).expect("Failed to parse UPDATER URL")],
|
||||
pubkey: String::from(UPDATER_PUBKEY),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Run auto updater
|
||||
if let Ok(current_version) = Version::parse(env!("CARGO_PKG_VERSION")) {
|
||||
if let Ok(Some(update)) = check_update(current_version, config) {
|
||||
if update.download_and_install().is_ok() {
|
||||
info!("Update installed")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.new(|cx| {
|
||||
let public_key = account.public_key();
|
||||
let relays = cx.new(|_| None);
|
||||
|
||||
@@ -19,7 +19,6 @@ use nostr_sdk::prelude::*;
|
||||
use smol::fs;
|
||||
use state::get_client;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::{Button, ButtonRounded, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
@@ -50,22 +49,39 @@ pub fn init(
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct ChatItem {
|
||||
profile: NostrProfile,
|
||||
struct ParsedMessage {
|
||||
avatar: SharedString,
|
||||
display_name: SharedString,
|
||||
created_at: SharedString,
|
||||
content: SharedString,
|
||||
ago: SharedString,
|
||||
}
|
||||
|
||||
impl ParsedMessage {
|
||||
pub fn new(profile: &NostrProfile, content: &str, created_at: Timestamp) -> Self {
|
||||
let avatar = profile.avatar().into();
|
||||
let display_name = profile.name().into();
|
||||
let content = SharedString::new(content);
|
||||
let created_at = LastSeen(created_at).human_readable();
|
||||
|
||||
Self {
|
||||
avatar,
|
||||
display_name,
|
||||
created_at,
|
||||
content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Message {
|
||||
Item(Box<ChatItem>),
|
||||
User(Box<ParsedMessage>),
|
||||
System(SharedString),
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn new(chat_message: ChatItem) -> Self {
|
||||
Self::Item(Box::new(chat_message))
|
||||
pub fn new(message: ParsedMessage) -> Self {
|
||||
Self::User(Box::new(message))
|
||||
}
|
||||
|
||||
pub fn system(content: SharedString) -> Self {
|
||||
@@ -287,12 +303,7 @@ impl Chat {
|
||||
|
||||
let old_len = self.messages.read(cx).len();
|
||||
let room = model.read(cx);
|
||||
let ago = LastSeen(Timestamp::now()).human_readable();
|
||||
let message = Message::new(ChatItem {
|
||||
profile: room.owner.clone(),
|
||||
content: content.into(),
|
||||
ago,
|
||||
});
|
||||
let message = Message::new(ParsedMessage::new(&room.owner, &content, Timestamp::now()));
|
||||
|
||||
// Update message list
|
||||
cx.update_entity(&self.messages, |this, cx| {
|
||||
@@ -337,11 +348,10 @@ impl Chat {
|
||||
room.owner.to_owned()
|
||||
};
|
||||
|
||||
Some(Message::new(ChatItem {
|
||||
profile: member,
|
||||
content: ev.content.into(),
|
||||
ago: LastSeen(ev.created_at).human_readable(),
|
||||
}))
|
||||
let message =
|
||||
Message::new(ParsedMessage::new(&member, &ev.content, ev.created_at));
|
||||
|
||||
Some(message)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -379,11 +389,11 @@ impl Chat {
|
||||
.iter()
|
||||
.filter_map(|event| {
|
||||
if let Some(profile) = room.member(&event.pubkey) {
|
||||
let message = Message::new(ChatItem {
|
||||
profile,
|
||||
content: event.content.clone().into(),
|
||||
ago: LastSeen(event.created_at).human_readable(),
|
||||
});
|
||||
let message = Message::new(ParsedMessage::new(
|
||||
&profile,
|
||||
&event.content,
|
||||
event.created_at,
|
||||
));
|
||||
|
||||
if !old_messages.iter().any(|old| old == &message) {
|
||||
Some(message)
|
||||
@@ -599,7 +609,7 @@ impl Chat {
|
||||
.w_full()
|
||||
.p_2()
|
||||
.map(|this| match message {
|
||||
Message::Item(item) => this
|
||||
Message::User(item) => this
|
||||
.hover(|this| this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE)))
|
||||
.child(
|
||||
div()
|
||||
@@ -614,7 +624,7 @@ impl Chat {
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
img(item.profile.avatar())
|
||||
img(item.avatar.clone())
|
||||
.size_8()
|
||||
.rounded_full()
|
||||
.flex_shrink_0(),
|
||||
@@ -631,8 +641,10 @@ impl Chat {
|
||||
.items_baseline()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.child(div().font_semibold().child(item.profile.name()))
|
||||
.child(div().child(item.ago.clone()).text_color(
|
||||
.child(
|
||||
div().font_semibold().child(item.display_name.clone()),
|
||||
)
|
||||
.child(div().child(item.created_at.clone()).text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)),
|
||||
)
|
||||
@@ -669,7 +681,7 @@ impl Chat {
|
||||
.text_center()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.line_height(relative(1.))
|
||||
.line_height(relative(1.2))
|
||||
.child(
|
||||
svg()
|
||||
.path("brand/coop.svg")
|
||||
|
||||
@@ -6,7 +6,6 @@ use gpui::{
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use state::get_client;
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::Button,
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
|
||||
@@ -6,7 +6,6 @@ use gpui::{
|
||||
use nostr_connect::prelude::*;
|
||||
use state::get_client;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::{Button, ButtonCustomVariant, ButtonVariants},
|
||||
input::{InputEvent, TextInput},
|
||||
|
||||
@@ -9,7 +9,6 @@ use nostr_sdk::prelude::*;
|
||||
use smol::fs;
|
||||
use state::get_client;
|
||||
use std::str::FromStr;
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
|
||||
@@ -4,7 +4,6 @@ use gpui::{
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use state::get_client;
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
input::{InputEvent, TextInput},
|
||||
|
||||
@@ -14,7 +14,6 @@ use serde::Deserialize;
|
||||
use smol::Timer;
|
||||
use state::get_client;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
use tokio::sync::oneshot;
|
||||
use ui::{
|
||||
button::{Button, ButtonRounded},
|
||||
input::{InputEvent, TextInput},
|
||||
|
||||
@@ -12,5 +12,6 @@ gpui.workspace = true
|
||||
nostr-sdk.workspace = true
|
||||
anyhow.workspace = true
|
||||
itertools.workspace = true
|
||||
smol.workspace = true
|
||||
chrono.workspace = true
|
||||
smol.workspace = true
|
||||
oneshot.workspace = true
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use anyhow::anyhow;
|
||||
use async_utility::tokio::sync::oneshot;
|
||||
use common::utils::{compare, room_hash, signer_public_key};
|
||||
use gpui::{App, AppContext, Context, Entity, Global};
|
||||
use itertools::Itertools;
|
||||
@@ -31,8 +30,10 @@ impl ChatRegistry {
|
||||
pub fn register(cx: &mut App) -> Entity<Self> {
|
||||
Self::global(cx).unwrap_or_else(|| {
|
||||
let entity = cx.new(Self::new);
|
||||
|
||||
// Set global state
|
||||
cx.set_global(GlobalChatRegistry(entity.clone()));
|
||||
|
||||
// Observe and load metadata for any new rooms
|
||||
cx.observe_new::<Room>(|this, _window, cx| {
|
||||
let client = get_client();
|
||||
@@ -74,7 +75,7 @@ impl ChatRegistry {
|
||||
|
||||
fn new(_cx: &mut Context<Self>) -> Self {
|
||||
Self {
|
||||
rooms: Vec::with_capacity(5),
|
||||
rooms: vec![],
|
||||
is_loading: true,
|
||||
}
|
||||
}
|
||||
@@ -185,6 +186,12 @@ impl ChatRegistry {
|
||||
});
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
// Re sort rooms by last seen
|
||||
self.rooms
|
||||
.sort_by_key(|room| Reverse(room.read(cx).last_seen()));
|
||||
|
||||
cx.notify();
|
||||
} else {
|
||||
let room = cx.new(|cx| Room::parse(&event, cx));
|
||||
self.rooms.insert(0, room);
|
||||
|
||||
@@ -124,6 +124,10 @@ impl Room {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_seen(&self) -> &LastSeen {
|
||||
&self.last_seen
|
||||
}
|
||||
|
||||
/// Get all public keys from current room
|
||||
pub fn pubkeys(&self) -> Vec<PublicKey> {
|
||||
let mut pubkeys: Vec<_> = self.members.iter().map(|m| m.public_key()).collect();
|
||||
|
||||
@@ -2,8 +2,6 @@ pub const KEYRING_SERVICE: &str = "Coop Safe Storage";
|
||||
pub const APP_NAME: &str = "Coop";
|
||||
pub const APP_ID: &str = "su.reya.coop";
|
||||
|
||||
pub const FAKE_SIG: &str = "f9e79d141c004977192d05a86f81ec7c585179c371f7350a5412d33575a2a356433f58e405c2296ed273e2fe0aafa25b641e39cc4e1f3f261ebf55bce0cbac83";
|
||||
|
||||
/// Subscriptions
|
||||
pub const NEW_MESSAGE_SUB_ID: &str = "listen_new_giftwraps";
|
||||
pub const ALL_MESSAGES_SUB_ID: &str = "listen_all_giftwraps";
|
||||
|
||||
@@ -2,6 +2,7 @@ use chrono::{Datelike, Local, TimeZone};
|
||||
use gpui::SharedString;
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct LastSeen(pub Timestamp);
|
||||
|
||||
impl LastSeen {
|
||||
|
||||
@@ -16,13 +16,14 @@ pub async fn signer_public_key(client: &Client) -> anyhow::Result<PublicKey, any
|
||||
}
|
||||
|
||||
pub async fn preload(client: &Client, public_key: PublicKey) -> anyhow::Result<(), anyhow::Error> {
|
||||
let sync_opts = SyncOptions::default();
|
||||
let subscription = Filter::new()
|
||||
.kind(Kind::ContactList)
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
// Get contact list
|
||||
_ = client.sync(subscription, &SyncOptions::default()).await;
|
||||
_ = client.sync(subscription, &sync_opts).await;
|
||||
|
||||
let all_messages_sub_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
|
||||
let new_message_sub_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
|
||||
|
||||
@@ -6,5 +6,4 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
nostr-sdk.workspace = true
|
||||
tokio.workspace = true
|
||||
dirs.workspace = true
|
||||
|
||||
@@ -4,23 +4,29 @@ use std::{fs, sync::OnceLock, time::Duration};
|
||||
|
||||
static CLIENT: OnceLock<Client> = OnceLock::new();
|
||||
|
||||
pub fn initialize_client() {
|
||||
pub fn initialize_client() -> &'static Client {
|
||||
// Setup app data folder
|
||||
let config_dir = config_dir().expect("Config directory not found");
|
||||
let _ = fs::create_dir_all(config_dir.join("Coop/"));
|
||||
let app_dir = config_dir.join("Coop/");
|
||||
|
||||
// Create app directory if it doesn't exist
|
||||
_ = fs::create_dir_all(&app_dir);
|
||||
|
||||
// Setup database
|
||||
let lmdb = NostrLMDB::open(config_dir.join("Coop/nostr")).expect("Database is NOT initialized");
|
||||
let lmdb = NostrLMDB::open(app_dir.join("nostr")).expect("Database is NOT initialized");
|
||||
|
||||
// Client options
|
||||
let opts = Options::new()
|
||||
// NIP-65
|
||||
.gossip(true)
|
||||
.max_avg_latency(Duration::from_secs(2));
|
||||
// Skip all very slow relays
|
||||
.max_avg_latency(Duration::from_millis(800));
|
||||
|
||||
// Setup Nostr Client
|
||||
let client = ClientBuilder::default().database(lmdb).opts(opts).build();
|
||||
|
||||
CLIENT.set(client).expect("Client is already initialized!");
|
||||
CLIENT.get().expect("Client is NOT initialized!")
|
||||
}
|
||||
|
||||
pub fn get_client() -> &'static Client {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, AnyView, App, AppContext, Axis, Context, Element, Entity,
|
||||
div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Entity,
|
||||
InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels,
|
||||
Point, Render, StatefulInteractiveElement, Style, Styled as _, WeakEntity, Window,
|
||||
};
|
||||
@@ -374,9 +374,7 @@ impl Render for Dock {
|
||||
})
|
||||
.map(|this| match &self.panel {
|
||||
DockItem::Split { view, .. } => this.child(view.clone()),
|
||||
DockItem::Tabs { view, .. } => {
|
||||
this.child(AnyView::from(view.clone()).cached(cache_style))
|
||||
}
|
||||
DockItem::Tabs { view, .. } => this.child(view.clone()),
|
||||
DockItem::Panel { view, .. } => this.child(view.clone().view().cached(cache_style)),
|
||||
})
|
||||
.child(self.render_resize_handle(window, cx))
|
||||
@@ -432,14 +430,20 @@ impl Element for DockElement {
|
||||
_: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::PrepaintState,
|
||||
window: &mut gpui::Window,
|
||||
_: &mut App,
|
||||
cx: &mut App,
|
||||
) {
|
||||
window.on_mouse_event({
|
||||
let view = self.view.clone();
|
||||
let is_resizing = view.read(cx).is_resizing;
|
||||
move |e: &MouseMoveEvent, phase, window, cx| {
|
||||
if phase.bubble() {
|
||||
view.update(cx, |view, cx| view.resize(e.position, window, cx))
|
||||
if !is_resizing {
|
||||
return;
|
||||
}
|
||||
if !phase.bubble() {
|
||||
return;
|
||||
}
|
||||
|
||||
view.update(cx, |view, cx| view.resize(e.position, window, cx))
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ impl RenderOnce for Modal {
|
||||
.with_easing(cubic_bezier(0.32, 0.72, 0., 1.)),
|
||||
move |this, delta| {
|
||||
let y_offset = px(0.) + delta * px(30.);
|
||||
this.top(y + y_offset).opacity(delta)
|
||||
this.top(y + y_offset)
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -133,7 +133,6 @@ impl PopupMenu {
|
||||
this.dismiss(&Dismiss, window, cx)
|
||||
}),
|
||||
];
|
||||
|
||||
let menu = Self {
|
||||
focus_handle,
|
||||
action_focus_handle: None,
|
||||
@@ -150,7 +149,7 @@ impl PopupMenu {
|
||||
scroll_state: Rc::new(Cell::new(ScrollbarState::default())),
|
||||
subscriptions,
|
||||
};
|
||||
window.refresh();
|
||||
|
||||
f(menu, window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -510,41 +510,38 @@ impl Element for ResizePanelGroupElement {
|
||||
let axis = self.axis;
|
||||
let current_ix = view.read(cx).resizing_panel_ix;
|
||||
move |e: &MouseMoveEvent, phase, window, cx| {
|
||||
if phase.bubble() {
|
||||
if let Some(ix) = current_ix {
|
||||
view.update(cx, |view, cx| {
|
||||
let panel = view
|
||||
.panels
|
||||
.get(ix)
|
||||
.expect("BUG: invalid panel index")
|
||||
.read(cx);
|
||||
|
||||
match axis {
|
||||
Axis::Horizontal => view.resize_panels(
|
||||
ix,
|
||||
e.position.x - panel.bounds.left(),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Axis::Vertical => {
|
||||
view.resize_panels(
|
||||
ix,
|
||||
e.position.y - panel.bounds.top(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if !phase.bubble() {
|
||||
return;
|
||||
}
|
||||
let Some(ix) = current_ix else { return };
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
let panel = view
|
||||
.panels
|
||||
.get(ix)
|
||||
.expect("BUG: invalid panel index")
|
||||
.read(cx);
|
||||
|
||||
match axis {
|
||||
Axis::Horizontal => {
|
||||
view.resize_panels(ix, e.position.x - panel.bounds.left(), window, cx)
|
||||
}
|
||||
Axis::Vertical => {
|
||||
view.resize_panels(ix, e.position.y - panel.bounds.top(), window, cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// When any mouse up, stop dragging
|
||||
window.on_mouse_event({
|
||||
let view = self.view.clone();
|
||||
let current_ix = view.read(cx).resizing_panel_ix;
|
||||
move |_: &MouseUpEvent, phase, window, cx| {
|
||||
if current_ix.is_none() {
|
||||
return;
|
||||
}
|
||||
if phase.bubble() {
|
||||
view.update(cx, |view, cx| view.done_resizing(window, cx));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
use gpui::*;
|
||||
use gpui::{
|
||||
fill, point, px, relative, App, Bounds, ContentMask, CursorStyle, Edges, Element, EntityId,
|
||||
Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels,
|
||||
Point, Position, ScrollHandle, ScrollWheelEvent, UniformListScrollHandle, Window,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cell::Cell, rc::Rc, time::Instant};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
|
||||
@@ -10,18 +18,24 @@ pub enum ScrollbarShow {
|
||||
#[default]
|
||||
Scrolling,
|
||||
Hover,
|
||||
Always,
|
||||
}
|
||||
|
||||
impl ScrollbarShow {
|
||||
fn is_hover(&self) -> bool {
|
||||
matches!(self, Self::Hover)
|
||||
}
|
||||
|
||||
fn is_always(&self) -> bool {
|
||||
matches!(self, Self::Always)
|
||||
}
|
||||
}
|
||||
|
||||
const BORDER_WIDTH: Pixels = px(0.);
|
||||
pub(crate) const WIDTH: Pixels = px(12.);
|
||||
const MIN_THUMB_SIZE: f32 = 80.;
|
||||
const THUMB_RADIUS: Pixels = Pixels(3.0);
|
||||
const THUMB_INSET: Pixels = Pixels(4.);
|
||||
const THUMB_RADIUS: Pixels = Pixels(4.0);
|
||||
const THUMB_INSET: Pixels = Pixels(3.);
|
||||
const FADE_OUT_DURATION: f32 = 3.0;
|
||||
const FADE_OUT_DELAY: f32 = 2.0;
|
||||
|
||||
@@ -65,6 +79,8 @@ pub struct ScrollbarState {
|
||||
drag_pos: Point<Pixels>,
|
||||
last_scroll_offset: Point<Pixels>,
|
||||
last_scroll_time: Option<Instant>,
|
||||
// Last update offset
|
||||
last_update: Instant,
|
||||
}
|
||||
|
||||
impl Default for ScrollbarState {
|
||||
@@ -76,6 +92,7 @@ impl Default for ScrollbarState {
|
||||
drag_pos: point(px(0.), px(0.)),
|
||||
last_scroll_offset: point(px(0.), px(0.)),
|
||||
last_scroll_time: None,
|
||||
last_update: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,8 +123,8 @@ impl ScrollbarState {
|
||||
fn with_hovered(&self, axis: Option<ScrollbarAxis>) -> Self {
|
||||
let mut state = *self;
|
||||
state.hovered_axis = axis;
|
||||
if self.is_scrollbar_visible() {
|
||||
state.last_scroll_time = Some(Instant::now());
|
||||
if axis.is_some() {
|
||||
state.last_scroll_time = Some(std::time::Instant::now());
|
||||
}
|
||||
state
|
||||
}
|
||||
@@ -115,6 +132,9 @@ impl ScrollbarState {
|
||||
fn with_hovered_on_thumb(&self, axis: Option<ScrollbarAxis>) -> Self {
|
||||
let mut state = *self;
|
||||
state.hovered_on_thumb = axis;
|
||||
if axis.is_some() {
|
||||
state.last_scroll_time = Some(std::time::Instant::now());
|
||||
}
|
||||
state
|
||||
}
|
||||
|
||||
@@ -135,7 +155,18 @@ impl ScrollbarState {
|
||||
state
|
||||
}
|
||||
|
||||
fn with_last_update(&self, t: Instant) -> Self {
|
||||
let mut state = *self;
|
||||
state.last_update = t;
|
||||
state
|
||||
}
|
||||
|
||||
fn is_scrollbar_visible(&self) -> bool {
|
||||
// On drag
|
||||
if self.dragged_axis.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(last_time) = self.last_scroll_time {
|
||||
let elapsed = Instant::now().duration_since(last_time).as_secs_f32();
|
||||
elapsed < FADE_OUT_DURATION
|
||||
@@ -178,9 +209,9 @@ impl ScrollbarAxis {
|
||||
match self {
|
||||
Self::Vertical => vec![Self::Vertical],
|
||||
Self::Horizontal => vec![Self::Horizontal],
|
||||
// This should keep vertical first, vertical is the primary axis
|
||||
// if vertical not need display, then horizontal will not keep right margin.
|
||||
Self::Both => vec![Self::Vertical, Self::Horizontal],
|
||||
// This should keep Horizontal first, Vertical is the primary axis
|
||||
// if Vertical not need display, then Horizontal will not keep right margin.
|
||||
Self::Both => vec![Self::Horizontal, Self::Vertical],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,11 +220,14 @@ impl ScrollbarAxis {
|
||||
pub struct Scrollbar {
|
||||
view_id: EntityId,
|
||||
axis: ScrollbarAxis,
|
||||
/// When is vertical, this is the height of the scrollbar.
|
||||
width: Pixels,
|
||||
scroll_handle: Rc<Box<dyn ScrollHandleOffsetable>>,
|
||||
scroll_size: gpui::Size<Pixels>,
|
||||
state: Rc<Cell<ScrollbarState>>,
|
||||
/// Maximum frames per second for scrolling by drag. Default is 120 FPS.
|
||||
///
|
||||
/// This is used to limit the update rate of the scrollbar when it is
|
||||
/// being dragged for some complex interactions for reducing CPU usage.
|
||||
max_fps: usize,
|
||||
}
|
||||
|
||||
impl Scrollbar {
|
||||
@@ -209,8 +243,8 @@ impl Scrollbar {
|
||||
state,
|
||||
axis,
|
||||
scroll_size,
|
||||
width: px(12.),
|
||||
scroll_handle: Rc::new(Box::new(scroll_handle)),
|
||||
max_fps: 120,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,11 +324,21 @@ impl Scrollbar {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set maximum frames per second for scrolling by drag. Default is 120 FPS.
|
||||
///
|
||||
/// If you have very high CPU usage, consider reducing this value to improve performance.
|
||||
///
|
||||
/// Available values: 30..120
|
||||
pub fn max_fps(mut self, max_fps: usize) -> Self {
|
||||
self.max_fps = max_fps.clamp(30, 120);
|
||||
self
|
||||
}
|
||||
|
||||
fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
(
|
||||
cx.theme().scrollbar_thumb_hover,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::SEVEN),
|
||||
THUMB_INSET - px(1.),
|
||||
THUMB_RADIUS,
|
||||
)
|
||||
@@ -304,7 +348,7 @@ impl Scrollbar {
|
||||
(
|
||||
cx.theme().scrollbar_thumb_hover,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::SIX),
|
||||
THUMB_INSET - px(1.),
|
||||
THUMB_RADIUS,
|
||||
)
|
||||
@@ -382,11 +426,11 @@ impl Element for Scrollbar {
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||
let style = Style {
|
||||
let style = gpui::Style {
|
||||
position: Position::Absolute,
|
||||
flex_grow: 1.0,
|
||||
flex_shrink: 1.0,
|
||||
size: Size {
|
||||
size: gpui::Size {
|
||||
width: relative(1.).into(),
|
||||
height: relative(1.).into(),
|
||||
},
|
||||
@@ -409,7 +453,6 @@ impl Element for Scrollbar {
|
||||
});
|
||||
|
||||
let mut states = vec![];
|
||||
|
||||
let mut has_both = self.axis.is_both();
|
||||
|
||||
for axis in self.axis.all().into_iter() {
|
||||
@@ -430,7 +473,7 @@ impl Element for Scrollbar {
|
||||
|
||||
// The horizontal scrollbar is set avoid overlapping with the vertical scrollbar, if the vertical scrollbar is visible.
|
||||
let margin_end = if has_both && !is_vertical {
|
||||
self.width
|
||||
WIDTH
|
||||
} else {
|
||||
px(0.)
|
||||
};
|
||||
@@ -449,31 +492,29 @@ impl Element for Scrollbar {
|
||||
|
||||
let bounds = Bounds {
|
||||
origin: if is_vertical {
|
||||
point(
|
||||
hitbox.origin.x + hitbox.size.width - self.width,
|
||||
hitbox.origin.y,
|
||||
)
|
||||
point(hitbox.origin.x + hitbox.size.width - WIDTH, hitbox.origin.y)
|
||||
} else {
|
||||
point(
|
||||
hitbox.origin.x,
|
||||
hitbox.origin.y + hitbox.size.height - self.width,
|
||||
hitbox.origin.y + hitbox.size.height - WIDTH,
|
||||
)
|
||||
},
|
||||
size: gpui::Size {
|
||||
width: if is_vertical {
|
||||
self.width
|
||||
WIDTH
|
||||
} else {
|
||||
hitbox.size.width
|
||||
},
|
||||
height: if is_vertical {
|
||||
hitbox.size.height
|
||||
} else {
|
||||
self.width
|
||||
WIDTH
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let state = self.state.clone();
|
||||
let is_always_to_show = cx.theme().scrollbar_show.is_always();
|
||||
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
|
||||
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
|
||||
let is_hovered_on_thumb = state.get().hovered_on_thumb == Some(axis);
|
||||
@@ -481,7 +522,9 @@ impl Element for Scrollbar {
|
||||
let (thumb_bg, bar_bg, bar_border, inset, radius) =
|
||||
if state.get().dragged_axis == Some(axis) {
|
||||
Self::style_for_active(cx)
|
||||
} else if is_hover_to_show && is_hovered_on_bar {
|
||||
} else if (is_hover_to_show || is_always_to_show)
|
||||
&& (is_hovered_on_bar || is_hovered_on_thumb)
|
||||
{
|
||||
if is_hovered_on_thumb {
|
||||
Self::style_for_hovered_thumb(cx)
|
||||
} else {
|
||||
@@ -520,12 +563,12 @@ impl Element for Scrollbar {
|
||||
let thumb_bounds = if is_vertical {
|
||||
Bounds::from_corners(
|
||||
point(bounds.origin.x, bounds.origin.y + thumb_start),
|
||||
point(bounds.origin.x + self.width, bounds.origin.y + thumb_end),
|
||||
point(bounds.origin.x + WIDTH, bounds.origin.y + thumb_end),
|
||||
)
|
||||
} else {
|
||||
Bounds::from_corners(
|
||||
point(bounds.origin.x + thumb_start, bounds.origin.y),
|
||||
point(bounds.origin.x + thumb_end, bounds.origin.y + self.width),
|
||||
point(bounds.origin.x + thumb_end, bounds.origin.y + WIDTH),
|
||||
)
|
||||
};
|
||||
let thumb_fill_bounds = if is_vertical {
|
||||
@@ -535,7 +578,7 @@ impl Element for Scrollbar {
|
||||
bounds.origin.y + thumb_start + inset,
|
||||
),
|
||||
point(
|
||||
bounds.origin.x + self.width - inset,
|
||||
bounds.origin.x + WIDTH - inset,
|
||||
bounds.origin.y + thumb_end - inset,
|
||||
),
|
||||
)
|
||||
@@ -547,7 +590,7 @@ impl Element for Scrollbar {
|
||||
),
|
||||
point(
|
||||
bounds.origin.x + thumb_end - inset,
|
||||
bounds.origin.y + self.width - inset,
|
||||
bounds.origin.y + WIDTH - inset,
|
||||
),
|
||||
)
|
||||
};
|
||||
@@ -589,6 +632,15 @@ impl Element for Scrollbar {
|
||||
let is_visible = self.state.get().is_scrollbar_visible();
|
||||
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
|
||||
|
||||
// Update last_scroll_time when offset is changed.
|
||||
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {
|
||||
self.state.set(
|
||||
self.state
|
||||
.get()
|
||||
.with_last_scroll(self.scroll_handle.offset(), Some(Instant::now())),
|
||||
);
|
||||
}
|
||||
|
||||
window.with_content_mask(
|
||||
Some(ContentMask {
|
||||
bounds: hitbox_bounds,
|
||||
@@ -711,30 +763,36 @@ impl Element for Scrollbar {
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
let state = self.state.clone();
|
||||
let view_id = self.view_id;
|
||||
let max_fps_duration = Duration::from_millis((1000 / self.max_fps) as u64);
|
||||
|
||||
move |event: &MouseMoveEvent, _, _, cx| {
|
||||
let mut notify = false;
|
||||
// When is hover to show mode or it was visible,
|
||||
// we need to update the hovered state and increase the last_scroll_time.
|
||||
let need_hover_to_update = is_hover_to_show || is_visible;
|
||||
// Update hovered state for scrollbar
|
||||
if bounds.contains(&event.position) {
|
||||
if bounds.contains(&event.position) && need_hover_to_update {
|
||||
state.set(state.get().with_hovered(Some(axis)));
|
||||
|
||||
if state.get().hovered_axis != Some(axis) {
|
||||
state.set(state.get().with_hovered(Some(axis)));
|
||||
cx.notify(view_id);
|
||||
notify = true;
|
||||
}
|
||||
} else if state.get().hovered_axis == Some(axis)
|
||||
&& state.get().hovered_axis.is_some()
|
||||
{
|
||||
state.set(state.get().with_hovered(None));
|
||||
cx.notify(view_id);
|
||||
notify = true;
|
||||
}
|
||||
|
||||
// Update hovered state for scrollbar thumb
|
||||
if thumb_bounds.contains(&event.position) {
|
||||
if state.get().hovered_on_thumb != Some(axis) {
|
||||
state.set(state.get().with_hovered_on_thumb(Some(axis)));
|
||||
cx.notify(view_id);
|
||||
notify = true;
|
||||
}
|
||||
} else if state.get().hovered_on_thumb == Some(axis) {
|
||||
state.set(state.get().with_hovered_on_thumb(None));
|
||||
cx.notify(view_id);
|
||||
notify = true;
|
||||
}
|
||||
|
||||
// Move thumb position on dragging
|
||||
@@ -769,10 +827,18 @@ impl Element for Scrollbar {
|
||||
if (scroll_handle.offset().y - offset.y).abs() > px(1.)
|
||||
|| (scroll_handle.offset().x - offset.x).abs() > px(1.)
|
||||
{
|
||||
scroll_handle.set_offset(offset);
|
||||
cx.notify(view_id);
|
||||
// Limit update rate
|
||||
if state.get().last_update.elapsed() > max_fps_duration {
|
||||
scroll_handle.set_offset(offset);
|
||||
state.set(state.get().with_last_update(Instant::now()));
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notify {
|
||||
cx.notify(view_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -121,7 +121,6 @@ impl RenderOnce for WindowBorder {
|
||||
.when(!tiling.bottom, |div| div.pb(SHADOW_SIZE))
|
||||
.when(!tiling.left, |div| div.pl(SHADOW_SIZE))
|
||||
.when(!tiling.right, |div| div.pr(SHADOW_SIZE))
|
||||
.on_mouse_move(|_e, window, _cx| window.refresh())
|
||||
.on_mouse_down(MouseButton::Left, move |_, window, _cx| {
|
||||
let size = window.window_bounds().get_bounds().size;
|
||||
let pos = window.mouse_position();
|
||||
|
||||
Reference in New Issue
Block a user