.
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m30s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m15s

This commit is contained in:
2026-02-07 20:52:17 +07:00
parent 253d04f988
commit 031883c278
10 changed files with 393 additions and 745 deletions

565
Cargo.lock generated
View File

@@ -85,12 +85,6 @@ dependencies = [
"equator",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -152,9 +146,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.100"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
[[package]]
name = "ar_archive_writer"
@@ -536,15 +530,6 @@ dependencies = [
"thiserror 2.0.18",
]
[[package]]
name = "atoi"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
dependencies = [
"num-traits",
]
[[package]]
name = "atomic"
version = "0.5.3"
@@ -1279,7 +1264,7 @@ dependencies = [
[[package]]
name = "collections"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"indexmap",
"rustc-hash 2.1.1",
@@ -1364,12 +1349,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-random"
version = "0.1.18"
@@ -1590,21 +1569,22 @@ dependencies = [
[[package]]
name = "cosmic-text"
version = "0.14.2"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8"
checksum = "8c5c9868e64aa6c5410629a83450e142c80e721c727a5bc0fb18107af6c2d66b"
dependencies = [
"bitflags 2.10.0",
"fontdb 0.16.2",
"fontdb",
"harfrust",
"linebender_resource_handle",
"log",
"rangemap",
"rustc-hash 1.1.0",
"rustybuzz 0.14.1",
"rustc-hash 2.1.1",
"self_cell",
"skrifa 0.40.0",
"smol_str",
"swash",
"sys-locale",
"ttf-parser 0.21.1",
"unicode-bidi",
"unicode-linebreak",
"unicode-script",
@@ -1620,21 +1600,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -1729,17 +1694,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204"
[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"pem-rfc7468",
"zeroize",
]
[[package]]
name = "derive_more"
version = "0.99.20"
@@ -1756,7 +1710,7 @@ dependencies = [
[[package]]
name = "derive_refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"proc-macro2",
"quote",
@@ -1788,7 +1742,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"subtle",
]
@@ -1893,12 +1846,6 @@ dependencies = [
"ui",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "downcast-rs"
version = "1.2.1"
@@ -1958,9 +1905,6 @@ name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
dependencies = [
"serde",
]
[[package]]
name = "embed-resource"
@@ -2078,17 +2022,6 @@ dependencies = [
"svg_fmt",
]
[[package]]
name = "etcetera"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
dependencies = [
"cfg-if",
"home",
"windows-sys 0.48.0",
]
[[package]]
name = "euclid"
version = "0.22.13"
@@ -2301,6 +2234,15 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "font-types"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e4d2d0cf79d38430cc9dc9aadec84774bff2e1ba30ae2bf6c16cfce9385a23"
dependencies = [
"bytemuck",
]
[[package]]
name = "fontconfig-parser"
version = "0.5.8"
@@ -2310,20 +2252,6 @@ dependencies = [
"roxmltree",
]
[[package]]
name = "fontdb"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3"
dependencies = [
"fontconfig-parser",
"log",
"memmap2 0.9.9",
"slotmap",
"tinyvec",
"ttf-parser 0.20.0",
]
[[package]]
name = "fontdb"
version = "0.23.0"
@@ -2335,7 +2263,7 @@ dependencies = [
"memmap2 0.9.9",
"slotmap",
"tinyvec",
"ttf-parser 0.25.1",
"ttf-parser",
]
[[package]]
@@ -2477,17 +2405,6 @@ dependencies = [
"futures-util",
]
[[package]]
name = "futures-intrusive"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
dependencies = [
"futures-core",
"lock_api",
"parking_lot",
]
[[package]]
name = "futures-io"
version = "0.3.31"
@@ -2711,7 +2628,7 @@ dependencies = [
[[package]]
name = "gpui"
version = "0.2.2"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"as-raw-xcb-connection",
@@ -2813,7 +2730,7 @@ dependencies = [
[[package]]
name = "gpui_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -2824,7 +2741,7 @@ dependencies = [
[[package]]
name = "gpui_tokio"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"gpui",
@@ -2869,6 +2786,19 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "harfrust"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f9f40651a03bc0f7316bd75267ff5767e93017ef3cfffe76c6aa7252cc5a31c"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
"core_maths",
"read-fonts 0.37.0",
"smallvec",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
@@ -2890,8 +2820,6 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
@@ -2901,15 +2829,6 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hashlink"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
dependencies = [
"hashbrown 0.15.5",
]
[[package]]
name = "heck"
version = "0.4.1"
@@ -3057,7 +2976,7 @@ dependencies = [
[[package]]
name = "http_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"async-compression",
@@ -3082,7 +3001,7 @@ dependencies = [
[[package]]
name = "http_client_tls"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"rustls",
"rustls-platform-verifier",
@@ -3629,15 +3548,10 @@ dependencies = [
]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
name = "linebender_resource_handle"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4"
[[package]]
name = "linicon"
@@ -3843,7 +3757,7 @@ dependencies = [
[[package]]
name = "media"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"bindgen",
@@ -4139,13 +4053,14 @@ dependencies = [
]
[[package]]
name = "nostr-gossip-sqlite"
name = "nostr-gossip-memory"
version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#9031e3684c661690a4c61865ac11d311456371e7"
dependencies = [
"indexmap",
"lru",
"nostr",
"nostr-gossip",
"sqlx",
"tokio",
]
@@ -4691,15 +4606,6 @@ dependencies = [
"hmac",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -4709,7 +4615,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "perf"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"collections",
"serde",
@@ -4836,27 +4742,6 @@ dependencies = [
"futures-io",
]
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -5315,7 +5200,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358"
dependencies = [
"bytemuck",
"font-types",
"font-types 0.10.1",
]
[[package]]
name = "read-fonts"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5"
dependencies = [
"bytemuck",
"core_maths",
"font-types 0.11.0",
]
[[package]]
@@ -5379,7 +5275,7 @@ dependencies = [
[[package]]
name = "refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"derive_refineable",
]
@@ -5478,7 +5374,7 @@ dependencies = [
[[package]]
name = "reqwest_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"bytes",
@@ -5533,7 +5429,7 @@ dependencies = [
[[package]]
name = "rope"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"arrayvec",
"log",
@@ -5551,26 +5447,6 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rsa"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core 0.6.4",
"signature",
"spki",
"subtle",
"zeroize",
]
[[package]]
name = "rust-embed"
version = "8.11.0"
@@ -5761,23 +5637,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rustybuzz"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
"libm",
"smallvec",
"ttf-parser 0.21.1",
"unicode-bidi-mirroring 0.2.0",
"unicode-ccc 0.2.0",
"unicode-properties",
"unicode-script",
]
[[package]]
name = "rustybuzz"
version = "0.20.1"
@@ -5789,9 +5648,9 @@ dependencies = [
"core_maths",
"log",
"smallvec",
"ttf-parser 0.25.1",
"unicode-bidi-mirroring 0.4.0",
"unicode-ccc 0.4.0",
"ttf-parser",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-properties",
"unicode-script",
]
@@ -5832,7 +5691,7 @@ dependencies = [
[[package]]
name = "scheduler"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"async-task",
"backtrace",
@@ -6179,16 +6038,6 @@ dependencies = [
"libc",
]
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core 0.6.4",
]
[[package]]
name = "simd-adler32"
version = "0.3.8"
@@ -6226,7 +6075,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841"
dependencies = [
"bytemuck",
"read-fonts",
"read-fonts 0.35.0",
]
[[package]]
name = "skrifa"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac"
dependencies = [
"bytemuck",
"read-fonts 0.37.0",
]
[[package]]
@@ -6249,9 +6108,6 @@ name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
dependencies = [
"serde",
]
[[package]]
name = "smol"
@@ -6272,9 +6128,9 @@ dependencies = [
[[package]]
name = "smol_str"
version = "0.2.2"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
checksum = "0f7a918bd2a9951d18ee6e48f076843e8e73a9a5d22cf05bcd4b7a81bdd04e17"
[[package]]
name = "socket2"
@@ -6313,204 +6169,6 @@ dependencies = [
"bitflags 2.10.0",
]
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "sqlx"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
dependencies = [
"sqlx-core",
"sqlx-macros",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
]
[[package]]
name = "sqlx-core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [
"base64",
"bytes",
"crc",
"crossbeam-queue",
"either",
"event-listener 5.4.1",
"futures-core",
"futures-intrusive",
"futures-io",
"futures-util",
"hashbrown 0.15.5",
"hashlink",
"indexmap",
"log",
"memchr",
"once_cell",
"percent-encoding",
"serde",
"serde_json",
"sha2",
"smallvec",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
"tracing",
"url",
]
[[package]]
name = "sqlx-macros"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
dependencies = [
"proc-macro2",
"quote",
"sqlx-core",
"sqlx-macros-core",
"syn 2.0.114",
]
[[package]]
name = "sqlx-macros-core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
dependencies = [
"dotenvy",
"either",
"heck 0.5.0",
"hex",
"once_cell",
"proc-macro2",
"quote",
"serde",
"serde_json",
"sha2",
"sqlx-core",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"syn 2.0.114",
"tokio",
"url",
]
[[package]]
name = "sqlx-mysql"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
dependencies = [
"atoi",
"base64",
"bitflags 2.10.0",
"byteorder",
"bytes",
"crc",
"digest",
"dotenvy",
"either",
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"generic-array",
"hex",
"hkdf",
"hmac",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"percent-encoding",
"rand 0.8.5",
"rsa",
"serde",
"sha1",
"sha2",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror 2.0.18",
"tracing",
"whoami",
]
[[package]]
name = "sqlx-postgres"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
dependencies = [
"atoi",
"base64",
"bitflags 2.10.0",
"byteorder",
"crc",
"dotenvy",
"etcetera",
"futures-channel",
"futures-core",
"futures-util",
"hex",
"hkdf",
"hmac",
"home",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"rand 0.8.5",
"serde",
"serde_json",
"sha2",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror 2.0.18",
"tracing",
"whoami",
]
[[package]]
name = "sqlx-sqlite"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [
"atoi",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"libsqlite3-sys",
"log",
"percent-encoding",
"serde",
"serde_urlencoded",
"sqlx-core",
"thiserror 2.0.18",
"tracing",
"url",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -6562,7 +6220,7 @@ dependencies = [
"gpui_tokio",
"log",
"nostr-connect",
"nostr-gossip-sqlite",
"nostr-gossip-memory",
"nostr-lmdb",
"nostr-sdk",
"petname",
@@ -6589,17 +6247,6 @@ dependencies = [
"float-cmp",
]
[[package]]
name = "stringprep"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
dependencies = [
"unicode-bidi",
"unicode-normalization",
"unicode-properties",
]
[[package]]
name = "strsim"
version = "0.11.1"
@@ -6658,7 +6305,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "sum_tree"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"arrayvec",
"log",
@@ -6767,7 +6414,7 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47846491253e976bdd07d0f9cc24b7daf24720d11309302ccbbc6e6b6e53550a"
dependencies = [
"skrifa",
"skrifa 0.37.0",
"yazi",
"zeno",
]
@@ -7393,18 +7040,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "ttf-parser"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
[[package]]
name = "ttf-parser"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
[[package]]
name = "ttf-parser"
version = "0.25.1"
@@ -7491,24 +7126,12 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe"
[[package]]
name = "unicode-ccc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]]
name = "unicode-ccc"
version = "0.4.0"
@@ -7604,13 +7227,13 @@ dependencies = [
"base64",
"data-url",
"flate2",
"fontdb 0.23.0",
"fontdb",
"imagesize",
"kurbo",
"log",
"pico-args",
"roxmltree",
"rustybuzz 0.20.1",
"rustybuzz",
"simplecss",
"siphasher",
"strict-num",
@@ -7643,7 +7266,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "util"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"async-fs",
@@ -7681,7 +7304,7 @@ dependencies = [
[[package]]
name = "util_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"perf",
"quote",
@@ -8125,7 +7748,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -9062,18 +8685,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
[[package]]
name = "zerocopy"
version = "0.8.38"
version = "0.8.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57cf3aa6855b23711ee9852dfc97dfaa51c45feaba5b645d0c777414d494a961"
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.38"
version = "0.8.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a616990af1a287837c4fe6596ad77ef57948f787e46ce28e166facc0cc1cb75"
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
dependencies = [
"proc-macro2",
"quote",
@@ -9157,7 +8780,7 @@ dependencies = [
[[package]]
name = "zlog"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"anyhow",
"chrono",
@@ -9174,7 +8797,7 @@ checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
[[package]]
name = "ztracing"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
dependencies = [
"tracing",
"tracing-subscriber",
@@ -9185,7 +8808,7 @@ dependencies = [
[[package]]
name = "ztracing_macro"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
source = "git+https://github.com/zed-industries/zed#7cb4d75139b39efb0d1d1e90faf7480d0f1dc734"
[[package]]
name = "zune-core"

View File

@@ -19,7 +19,7 @@ reqwest_client = { git = "https://github.com/zed-industries/zed" }
nostr-lmdb = { 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 = [ "nip96", "nip59", "nip49", "nip44" ] }
nostr-gossip-sqlite = { git = "https://github.com/rust-nostr/nostr" }
nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" }
# Others
anyhow = "1.0.44"

View File

@@ -146,15 +146,15 @@ impl ChatRegistry {
})
.ok();
}
NostrEvent::Eose => {
NostrEvent::Unwrapping(status) => {
this.update(cx, |this, cx| {
this.set_loading(status, cx);
this.get_rooms(cx);
})
.ok();
}
NostrEvent::Unwrapping(status) => {
NostrEvent::Eose => {
this.update(cx, |this, cx| {
this.set_loading(status, cx);
this.get_rooms(cx);
})
.ok();
@@ -310,10 +310,24 @@ impl ChatRegistry {
/// Add a new room to the start of list.
pub fn add_room<I>(&mut self, room: I, cx: &mut Context<Self>)
where
I: Into<Room>,
I: Into<Room> + 'static,
{
self.rooms.insert(0, cx.new(|_| room.into()));
cx.notify();
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
self.tasks.push(cx.spawn(async move |this, cx| {
if let Some(signer) = client.signer() {
if let Ok(public_key) = signer.get_public_key().await {
this.update(cx, |this, cx| {
this.rooms
.insert(0, cx.new(|_| room.into().organize(&public_key)));
cx.emit(ChatEvent::Ping);
cx.notify();
})
.ok();
}
}
}));
}
/// Emit an open room event.
@@ -407,23 +421,20 @@ impl ChatRegistry {
pub fn get_rooms(&mut self, cx: &mut Context<Self>) {
let task = self.get_rooms_from_database(cx);
self.tasks.push(
// Run and finished in the background
cx.spawn(async move |this, cx| {
match task.await {
Ok(rooms) => {
this.update(cx, move |this, cx| {
this.extend_rooms(rooms, cx);
this.sort(cx);
})
.ok();
}
Err(e) => {
log::error!("Failed to load rooms: {e}")
}
};
}),
);
self.tasks.push(cx.spawn(async move |this, cx| {
match task.await {
Ok(rooms) => {
this.update(cx, move |this, cx| {
this.extend_rooms(rooms, cx);
this.sort(cx);
})
.ok();
}
Err(e) => {
log::error!("Failed to load rooms: {e}")
}
};
}));
}
/// Create a task to load rooms from the database
@@ -434,8 +445,11 @@ impl ChatRegistry {
cx.background_spawn(async move {
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
// Get contacts
let contacts = client.database().contacts_public_keys(public_key).await?;
// Construct authored filter
let authored_filter = Filter::new()
.kind(Kind::ApplicationSpecificData)
.custom_tag(SingleLetterTag::lowercase(Alphabet::A), public_key);
@@ -443,6 +457,7 @@ impl ChatRegistry {
// Get all authored events
let authored = client.database().query(authored_filter).await?;
// Construct addressed filter
let addressed_filter = Filter::new()
.kind(Kind::ApplicationSpecificData)
.custom_tag(SingleLetterTag::lowercase(Alphabet::P), public_key);
@@ -453,6 +468,7 @@ impl ChatRegistry {
// Merge authored and addressed events
let events = authored.merge(addressed);
// Collect results
let mut rooms: HashSet<Room> = HashSet::new();
let mut grouped: HashMap<u64, Vec<UnsignedEvent>> = HashMap::new();
@@ -468,24 +484,21 @@ impl ChatRegistry {
for (_id, mut messages) in grouped.into_iter() {
messages.sort_by_key(|m| Reverse(m.created_at));
// Always use the latest message
let Some(latest) = messages.first() else {
continue;
};
let mut room = Room::from(latest);
if rooms.iter().any(|r| r.id == room.id) {
continue;
}
let mut public_keys = room.members();
public_keys.retain(|pk| pk != &public_key);
// Construct the room from the latest message.
//
// Call `.organize` to ensure the current user is at the end of the list.
let mut room = Room::from(latest).organize(&public_key);
// Check if the user has responded to the room
let user_sent = messages.iter().any(|m| m.pubkey == public_key);
// Check if public keys are from the user's contacts
let is_contact = public_keys.iter().any(|k| contacts.contains(k));
let is_contact = room.members.iter().any(|k| contacts.contains(k));
// Set the room's kind based on status
if user_sent || is_contact {
@@ -499,6 +512,24 @@ impl ChatRegistry {
})
}
/// Parse a nostr event into a message and push it to the belonging room
///
/// If the room doesn't exist, it will be created.
/// Updates room ordering based on the most recent messages.
pub fn new_message(&mut self, message: NewMessage, cx: &mut Context<Self>) {
match self.rooms.iter().find(|e| e.read(cx).id == message.room) {
Some(room) => {
room.update(cx, |this, cx| {
this.push_message(message, cx);
});
}
None => {
// Push the new room to the front of the list
self.add_room(message.rumor, cx);
}
}
}
/// Trigger a refresh of the opened chat rooms by their IDs
pub fn refresh_rooms(&mut self, ids: Option<Vec<u64>>, cx: &mut Context<Self>) {
if let Some(ids) = ids {
@@ -512,53 +543,6 @@ impl ChatRegistry {
}
}
/// Parse a nostr event into a message and push it to the belonging room
///
/// If the room doesn't exist, it will be created.
/// Updates room ordering based on the most recent messages.
pub fn new_message(&mut self, message: NewMessage, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
// Get the unique id
let id = message.rumor.uniq_id();
// Get the author
let author = message.rumor.pubkey;
match self.rooms.iter().find(|room| room.read(cx).id == id) {
Some(room) => {
let new_message = message.rumor.created_at > room.read(cx).created_at;
let created_at = message.rumor.created_at;
// Update room
room.update(cx, |this, cx| {
// Update the last timestamp if the new message is newer
if new_message {
this.set_created_at(created_at, cx);
}
// Set this room is ongoing if the new message is from current user
// if author == nostr.read(cx).identity().read(cx).public_key() {
// this.set_ongoing(cx);
// }
// Emit the new message to the room
this.emit_message(message, cx);
});
// Resort all rooms in the registry by their created at (after updated)
if new_message {
self.sort(cx);
}
}
None => {
// Push the new room to the front of the list
self.add_room(&message.rumor, cx);
// Notify the UI about the new room
cx.emit(ChatEvent::Ping);
}
}
}
/// Unwraps a gift-wrapped event and processes its contents.
async fn extract_rumor(
client: &Client,

View File

@@ -1,17 +1,25 @@
use std::hash::Hash;
use common::EventUtils;
use nostr_sdk::prelude::*;
/// New message.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct NewMessage {
pub gift_wrap: EventId,
pub room: u64,
pub rumor: UnsignedEvent,
}
impl NewMessage {
pub fn new(gift_wrap: EventId, rumor: UnsignedEvent) -> Self {
Self { gift_wrap, rumor }
let room = rumor.uniq_id();
Self {
gift_wrap,
room,
rumor,
}
}
}

View File

@@ -11,7 +11,7 @@ use nostr_sdk::prelude::*;
use person::{Person, PersonRegistry};
use state::{tracker, NostrRegistry};
use crate::NewMessage;
use crate::{ChatRegistry, NewMessage};
const SEND_RETRY: usize = 10;
@@ -99,16 +99,20 @@ pub enum RoomKind {
Ongoing,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Room {
/// Conversation ID
pub id: u64,
/// The timestamp of the last message in the room
pub created_at: Timestamp,
/// Subject of the room
pub subject: Option<SharedString>,
/// All members of the room
pub members: Vec<PublicKey>,
pub(super) members: Vec<PublicKey>,
/// Kind
pub kind: RoomKind,
}
@@ -145,11 +149,7 @@ impl From<&UnsignedEvent> for Room {
fn from(val: &UnsignedEvent) -> Self {
let id = val.uniq_id();
let created_at = val.created_at;
// Get the members from the event's tags and event's pubkey
let members = val.extract_public_keys();
// Get subject from tags
let subject = val
.tags
.find(TagKind::Subject)
@@ -165,6 +165,12 @@ impl From<&UnsignedEvent> for Room {
}
}
impl From<UnsignedEvent> for Room {
fn from(val: UnsignedEvent) -> Self {
Room::from(&val)
}
}
impl Room {
/// Constructs a new room with the given receiver and tags.
pub fn new<T>(author: PublicKey, receivers: T) -> Self
@@ -172,16 +178,30 @@ impl Room {
T: IntoIterator<Item = PublicKey>,
{
let tags = Tags::from_list(receivers.into_iter().map(Tag::public_key).collect());
// Construct an unsigned event for a direct message
//
// WARNING: never sign this event
let mut event = EventBuilder::new(Kind::PrivateDirectMessage, "")
.tags(tags)
.build(author);
// Generate event ID
// Ensure that the ID is set
event.ensure_id();
Room::from(&event)
}
/// Organizes the members of the room by moving the target member to the end.
///
/// Always call this function to ensure the current user is at the end of the list.
pub fn organize(mut self, target: &PublicKey) -> Self {
if let Some(index) = self.members.iter().position(|member| member == target) {
let member = self.members.remove(index);
self.members.push(member);
}
self
}
/// Sets the kind of the room and returns the modified room
pub fn kind(mut self, kind: RoomKind) -> Self {
self.kind = kind;
@@ -275,9 +295,21 @@ impl Room {
}
}
/// Emits a new message signal to the current room
pub fn emit_message(&self, message: NewMessage, cx: &mut Context<Self>) {
/// Push a new message to the current room
pub fn push_message(&mut self, message: NewMessage, cx: &mut Context<Self>) {
let created_at = message.rumor.created_at;
let new_message = created_at > self.created_at;
// Emit the incoming message event
cx.emit(RoomEvent::Incoming(message));
if new_message {
self.set_created_at(created_at, cx);
// Sort chats after emitting a new message
ChatRegistry::global(cx).update(cx, |this, cx| {
this.sort(cx);
});
}
}
/// Emits a signal to reload the current room's messages.

View File

@@ -12,44 +12,6 @@ const SECONDS_IN_MINUTE: i64 = 60;
const MINUTES_IN_HOUR: i64 = 60;
const HOURS_IN_DAY: i64 = 24;
const DAYS_IN_MONTH: i64 = 30;
const IMAGE_RESIZER: &str = "https://wsrv.nl";
pub trait RenderedProfile {
fn avatar(&self) -> SharedString;
fn display_name(&self) -> SharedString;
}
impl RenderedProfile for Profile {
fn avatar(&self) -> SharedString {
self.metadata()
.picture
.as_ref()
.filter(|picture| !picture.is_empty())
.map(|picture| {
let url = format!(
"{IMAGE_RESIZER}/?url={picture}&w=100&h=100&fit=cover&mask=circle&n=-1"
);
url.into()
})
.unwrap_or_else(|| "brand/avatar.png".into())
}
fn display_name(&self) -> SharedString {
if let Some(display_name) = self.metadata().display_name.as_ref() {
if !display_name.is_empty() {
return SharedString::from(display_name);
}
}
if let Some(name) = self.metadata().name.as_ref() {
if !name.is_empty() {
return SharedString::from(name);
}
}
SharedString::from(shorten_pubkey(self.public_key(), 4))
}
}
pub trait RenderedTimestamp {
fn to_human_time(&self) -> SharedString;

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::time::Duration;
use anyhow::{Context as AnyhowContext, Error};
use common::{shorten_pubkey, RenderedProfile, RenderedTimestamp, BOOTSTRAP_RELAYS};
use common::{shorten_pubkey, RenderedTimestamp, BOOTSTRAP_RELAYS};
use gpui::prelude::FluentBuilder;
use gpui::{
div, px, relative, rems, uniform_list, App, AppContext, Context, Div, Entity,
@@ -11,7 +11,7 @@ use gpui::{
use nostr_sdk::prelude::*;
use person::{Person, PersonRegistry};
use smallvec::{smallvec, SmallVec};
use state::{NostrAddress, NostrRegistry};
use state::{NostrAddress, NostrRegistry, TIMEOUT};
use theme::ActiveTheme;
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants};
@@ -22,56 +22,117 @@ pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<
cx.new(|cx| Screening::new(public_key, window, cx))
}
/// Screening
pub struct Screening {
profile: Person,
/// Public Key of the person being screened.
public_key: PublicKey,
/// Whether the person's address is verified.
verified: bool,
/// Whether the person is followed by current user.
followed: bool,
/// Last time the person was active.
last_active: Option<Timestamp>,
mutual_contacts: Vec<Profile>,
_tasks: SmallVec<[Task<()>; 3]>,
/// All mutual contacts of the person being screened.
mutual_contacts: Vec<PublicKey>,
/// Async tasks
tasks: SmallVec<[Task<()>; 3]>,
}
impl Screening {
pub fn new(public_key: PublicKey, window: &mut Window, cx: &mut Context<Self>) -> Self {
let http_client = cx.http_client();
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let persons = PersonRegistry::global(cx);
let profile = persons.read(cx).get(&public_key, cx);
let mut tasks = smallvec![];
// Check WOT
let contact_check: Task<Result<(bool, Vec<Profile>), Error>> = cx.background_spawn({
let client = nostr.read(cx).client();
async move {
let signer = client.signer().context("Signer not found")?;
let signer_pubkey = signer.get_public_key().await?;
// Check if user is in contact list
let contacts = client.database().contacts_public_keys(signer_pubkey).await;
let followed = contacts.unwrap_or_default().contains(&public_key);
// Check mutual contacts
let contact_list = Filter::new().kind(Kind::ContactList).pubkey(public_key);
let mut mutual_contacts = vec![];
if let Ok(events) = client.database().query(contact_list).await {
for event in events.into_iter().filter(|ev| ev.pubkey != signer_pubkey) {
if let Ok(metadata) = client.database().metadata(event.pubkey).await {
let profile = Profile::new(event.pubkey, metadata.unwrap_or_default());
mutual_contacts.push(profile);
}
}
}
Ok((followed, mutual_contacts))
}
cx.defer_in(window, move |this, _window, cx| {
this.check_contact(cx);
this.check_wot(cx);
this.check_last_activity(cx);
this.verify_identifier(cx);
});
// Check the last activity
let activity_check = cx.background_spawn(async move {
Self {
public_key,
verified: false,
followed: false,
last_active: None,
mutual_contacts: vec![],
tasks: smallvec![],
}
}
fn check_contact(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let public_key = self.public_key;
let task: Task<Result<bool, Error>> = cx.background_spawn(async move {
let signer = client.signer().context("Signer not found")?;
let signer_pubkey = signer.get_public_key().await?;
// Check if user is in contact list
let contacts = client.database().contacts_public_keys(signer_pubkey).await;
let followed = contacts.unwrap_or_default().contains(&public_key);
Ok(followed)
});
self.tasks.push(cx.spawn(async move |this, cx| {
let result = task.await.unwrap_or(false);
this.update(cx, |this, cx| {
this.followed = result;
cx.notify();
})
.ok();
}));
}
fn check_wot(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let public_key = self.public_key;
let task: Task<Result<Vec<PublicKey>, Error>> = cx.background_spawn(async move {
let signer = client.signer().context("Signer not found")?;
let signer_pubkey = signer.get_public_key().await?;
// Check mutual contacts
let filter = Filter::new().kind(Kind::ContactList).pubkey(public_key);
let mut mutual_contacts = vec![];
if let Ok(events) = client.database().query(filter).await {
for event in events.into_iter().filter(|ev| ev.pubkey != signer_pubkey) {
mutual_contacts.push(event.pubkey);
}
}
Ok(mutual_contacts)
});
self.tasks.push(cx.spawn(async move |this, cx| {
match task.await {
Ok(contacts) => {
this.update(cx, |this, cx| {
this.mutual_contacts = contacts;
cx.notify();
})
.ok();
}
Err(e) => {
log::error!("Failed to fetch mutual contacts: {}", e);
}
};
}));
}
fn check_last_activity(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let public_key = self.public_key;
let task: Task<Option<Timestamp>> = cx.background_spawn(async move {
let filter = Filter::new().author(public_key).limit(1);
let mut activity: Option<Timestamp> = None;
@@ -83,7 +144,7 @@ impl Screening {
if let Ok(mut stream) = client
.stream_events(target)
.timeout(Duration::from_secs(2))
.timeout(Duration::from_secs(TIMEOUT))
.await
{
while let Some((_url, event)) = stream.next().await {
@@ -96,78 +157,61 @@ impl Screening {
activity
});
// Verify the NIP05 address if available
let addr_check = profile.metadata().nip05.and_then(|address| {
Nip05Address::parse(&address).ok().map(|addr| {
cx.background_spawn(async move { addr.verify(&http_client, &public_key).await })
self.tasks.push(cx.spawn(async move |this, cx| {
let result = task.await;
this.update(cx, |this, cx| {
this.last_active = result;
cx.notify();
})
});
tasks.push(
// Run the contact check in the background
cx.spawn_in(window, async move |this, cx| {
if let Ok((followed, mutual_contacts)) = contact_check.await {
this.update(cx, |this, cx| {
this.followed = followed;
this.mutual_contacts = mutual_contacts;
cx.notify();
})
.ok();
}
}),
);
tasks.push(
// Run the activity check in the background
cx.spawn_in(window, async move |this, cx| {
let active = activity_check.await;
this.update(cx, |this, cx| {
this.last_active = active;
cx.notify();
})
.ok();
}),
);
tasks.push(
// Run the NIP-05 verification in the background
cx.spawn_in(window, async move |this, cx| {
if let Some(task) = addr_check {
if let Ok(verified) = task.await {
this.update(cx, |this, cx| {
this.verified = verified;
cx.notify();
})
.ok();
}
}
}),
);
Self {
profile,
verified: false,
followed: false,
last_active: None,
mutual_contacts: vec![],
_tasks: tasks,
}
.ok();
}));
}
fn address(&self, _cx: &Context<Self>) -> Option<String> {
self.profile.metadata().nip05
fn verify_identifier(&mut self, cx: &mut Context<Self>) {
let http_client = cx.http_client();
let public_key = self.public_key;
// Skip if the user doesn't have a NIP-05 identifier
let Some(address) = self.address(cx) else {
return;
};
let task: Task<Result<bool, Error>> =
cx.background_spawn(async move { address.verify(&http_client, &public_key).await });
self.tasks.push(cx.spawn(async move |this, cx| {
let result = task.await.unwrap_or(false);
this.update(cx, |this, cx| {
this.verified = result;
cx.notify();
})
.ok();
}));
}
fn open_njump(&mut self, _window: &mut Window, cx: &mut App) {
let Ok(bech32) = self.profile.public_key().to_bech32();
fn profile(&self, cx: &Context<Self>) -> Person {
let persons = PersonRegistry::global(cx);
persons.read(cx).get(&self.public_key, cx)
}
fn address(&self, cx: &Context<Self>) -> Option<Nip05Address> {
self.profile(cx)
.metadata()
.nip05
.and_then(|addr| Nip05Address::parse(&addr).ok())
}
fn open_njump(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
let Ok(bech32) = self.profile(cx).public_key().to_bech32();
cx.open_url(&format!("https://njump.me/{bech32}"));
}
fn report(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let public_key = self.profile.public_key();
let public_key = self.public_key;
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let tag = Tag::public_key_report(public_key, Report::Impersonation);
@@ -180,7 +224,7 @@ impl Screening {
Ok(())
});
cx.spawn_in(window, async move |_, cx| {
self.tasks.push(cx.spawn_in(window, async move |_, cx| {
if task.await.is_ok() {
cx.update(|window, cx| {
window.close_modal(cx);
@@ -188,8 +232,7 @@ impl Screening {
})
.ok();
}
})
.detach();
}));
}
fn mutual_contacts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -202,25 +245,27 @@ impl Screening {
this.title(SharedString::from("Mutual contacts")).child(
v_flex().gap_1().pb_4().child(
uniform_list("contacts", total, move |range, _window, cx| {
let persons = PersonRegistry::global(cx);
let mut items = Vec::with_capacity(total);
for ix in range {
if let Some(contact) = contacts.get(ix) {
items.push(
h_flex()
.h_11()
.w_full()
.px_2()
.gap_1p5()
.rounded(cx.theme().radius)
.text_sm()
.hover(|this| {
this.bg(cx.theme().elevated_surface_background)
})
.child(Avatar::new(contact.avatar()).size(rems(1.75)))
.child(contact.display_name()),
);
}
let Some(contact) = contacts.get(ix) else {
continue;
};
let profile = persons.read(cx).get(contact, cx);
items.push(
h_flex()
.h_11()
.w_full()
.px_2()
.gap_1p5()
.rounded(cx.theme().radius)
.text_sm()
.hover(|this| this.bg(cx.theme().elevated_surface_background))
.child(Avatar::new(profile.avatar()).size(rems(1.75)))
.child(profile.name()),
);
}
items
@@ -234,7 +279,9 @@ impl Screening {
impl Render for Screening {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let shorten_pubkey = shorten_pubkey(self.profile.public_key(), 8);
let profile = self.profile(cx);
let shorten_pubkey = shorten_pubkey(self.public_key, 8);
let total_mutuals = self.mutual_contacts.len();
let last_active = self.last_active.map(|_| true);
@@ -246,12 +293,12 @@ impl Render for Screening {
.items_center()
.justify_center()
.text_center()
.child(Avatar::new(self.profile.avatar()).size(rems(4.)))
.child(Avatar::new(profile.avatar()).size(rems(4.)))
.child(
div()
.font_semibold()
.line_height(relative(1.25))
.child(self.profile.name()),
.child(profile.name()),
),
)
.child(

View File

@@ -5,6 +5,8 @@ use gpui::SharedString;
use nostr_sdk::prelude::*;
use state::Announcement;
const IMAGE_RESIZER: &str = "https://wsrv.nl";
/// Person
#[derive(Debug, Clone)]
pub struct Person {
@@ -86,7 +88,12 @@ impl Person {
.picture
.as_ref()
.filter(|picture| !picture.is_empty())
.map(|picture| picture.into())
.map(|picture| {
let url = format!(
"{IMAGE_RESIZER}/?url={picture}&w=100&h=100&fit=cover&mask=circle&n=-1"
);
url.into()
})
.unwrap_or_else(|| "brand/avatar.png".into())
}

View File

@@ -10,7 +10,7 @@ common = { path = "../common" }
nostr-sdk.workspace = true
nostr-lmdb.workspace = true
nostr-connect.workspace = true
nostr-gossip-sqlite.workspace = true
nostr-gossip-memory.workspace = true
gpui.workspace = true
gpui_tokio.workspace = true

View File

@@ -6,9 +6,8 @@ use std::time::Duration;
use anyhow::{anyhow, Context as AnyhowContext, Error};
use common::{config_dir, CLIENT_NAME};
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task};
use gpui_tokio::Tokio;
use nostr_connect::prelude::*;
use nostr_gossip_sqlite::prelude::*;
use nostr_gossip_memory::prelude::*;
use nostr_lmdb::NostrLmdb;
use nostr_sdk::prelude::*;
@@ -125,20 +124,6 @@ impl NostrRegistry {
.expect("Failed to initialize database")
});
// Use tokio to spawn a task to build the gossip instance
let build_gossip_sqlite = Tokio::spawn(cx, async move {
NostrGossipSqlite::open(config_dir().join("gossip"))
.await
.expect("Failed to initialize gossip")
});
// Initialize the nostr gossip instance
let gossip = cx.foreground_executor().block_on(async move {
build_gossip_sqlite
.await
.expect("Failed to initialize gossip")
});
// Construct the nostr signer
let app_keys = Self::create_or_init_app_keys().unwrap_or(Keys::generate());
let signer = Arc::new(CoopSigner::new(app_keys.clone()));
@@ -150,7 +135,7 @@ impl NostrRegistry {
// Construct the nostr client
let client = ClientBuilder::default()
.signer(signer.clone())
.gossip(gossip)
.gossip(NostrGossipMemory::unbounded())
.database(lmdb)
.automatic_authentication(false)
.verify_subscriptions(false)