6 Commits
Author SHA1 Message Date
reya 853ab7a60e chore: bump version 2025-09-13 10:18:33 +07:00
reya d38e70ecbf chore: update deps 2025-09-13 07:51:33 +07:00
reyaandGitHub b142982ab1 chore: refactor event fetching (#148)
* use stream for nip65 and nip17 relays fetching

* .
2025-09-13 07:42:17 +07:00
reyaandGitHub 2ea2519e8b feat: resend failed messages (#147)
* .

* .

* fix

* fix

* update

* fix

* .

* .
2025-09-12 17:07:57 +07:00
reyaandGitHub 2ea5feaf4b chore: improve handling of user profiles (#146)
* resubscribe metadata for all pubkeys

* .
2025-09-10 10:06:45 +07:00
reya 4ec7530b91 chore: update deps 2025-09-10 08:21:43 +07:00
14 changed files with 609 additions and 441 deletions
+1 -1
View File
@@ -157,7 +157,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
name: Release ${{ steps.version.outputs.tag }}
name: ${{ steps.version.outputs.tag }}
draft: true
prerelease: false
generate_release_notes: true
Generated
+173 -152
View File
@@ -76,12 +76,6 @@ dependencies = [
"equator",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -176,9 +170,27 @@ dependencies = [
"zbus",
]
[[package]]
name = "ashpd"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0986d5b4f0802160191ad75f8d33ada000558757db3defb70299ca95d9fcbd"
dependencies = [
"async-fs",
"async-net",
"enumflags2",
"futures-channel",
"futures-util",
"rand 0.9.2",
"serde",
"serde_repr",
"url",
"zbus",
]
[[package]]
name = "assets"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"gpui",
@@ -261,7 +273,7 @@ dependencies = [
"futures-lite 2.6.1",
"parking",
"polling",
"rustix 1.0.8",
"rustix 1.1.2",
"slab",
"windows-sys 0.60.2",
]
@@ -303,7 +315,7 @@ dependencies = [
"cfg-if",
"event-listener",
"futures-lite 2.6.1",
"rustix 1.0.8",
"rustix 1.1.2",
]
[[package]]
@@ -329,7 +341,7 @@ dependencies = [
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.0.8",
"rustix 1.1.2",
"signal-hook-registry",
"slab",
"windows-sys 0.60.2",
@@ -416,7 +428,7 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auto_update"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"cargo-packager-updater",
@@ -460,9 +472,9 @@ dependencies = [
[[package]]
name = "aws-lc-rs"
version = "1.13.3"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -470,11 +482,11 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.30.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd"
dependencies = [
"bindgen 0.69.5",
"bindgen 0.72.1",
"cc",
"cmake",
"dunce",
@@ -525,32 +537,29 @@ checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
[[package]]
name = "bindgen"
version = "0.69.5"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags 2.9.4",
"cexpr",
"clang-sys",
"itertools 0.12.1",
"lazy_static",
"lazycell",
"itertools 0.13.0",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash 1.1.0",
"rustc-hash 2.1.1",
"shlex",
"syn 2.0.106",
"which",
]
[[package]]
name = "bindgen"
version = "0.71.1"
version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags 2.9.4",
"cexpr",
@@ -903,9 +912,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.36"
version = "1.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -985,16 +994,15 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.41"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link 0.1.3",
"windows-link 0.2.0",
]
[[package]]
@@ -1021,7 +1029,7 @@ dependencies = [
[[package]]
name = "client_keys"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"global",
@@ -1123,7 +1131,7 @@ dependencies = [
[[package]]
name = "collections"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"indexmap",
"rustc-hash 2.1.1",
@@ -1158,7 +1166,7 @@ dependencies = [
[[package]]
name = "common"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"chrono",
@@ -1231,7 +1239,7 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "coop"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"assets",
@@ -1564,7 +1572,7 @@ dependencies = [
[[package]]
name = "derive_refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"proc-macro2",
"quote",
@@ -1826,12 +1834,12 @@ dependencies = [
[[package]]
name = "errno"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.60.2",
"windows-sys 0.61.0",
]
[[package]]
@@ -2342,7 +2350,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55"
dependencies = [
"rustix 1.0.8",
"rustix 1.1.2",
"windows-targets 0.52.6",
]
@@ -2369,7 +2377,7 @@ dependencies = [
"js-sys",
"libc",
"r-efi",
"wasi 0.14.4+wasi-0.2.4",
"wasi 0.14.5+wasi-0.2.4",
"wasm-bindgen",
]
@@ -2397,7 +2405,7 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "global"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"dirs 5.0.1",
@@ -2490,11 +2498,11 @@ dependencies = [
[[package]]
name = "gpui"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"as-raw-xcb-connection",
"ashpd",
"ashpd 0.11.0",
"async-task",
"bindgen 0.71.1",
"blade-graphics",
@@ -2584,7 +2592,7 @@ dependencies = [
[[package]]
name = "gpui_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -2596,7 +2604,7 @@ dependencies = [
[[package]]
name = "gpui_tokio"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"gpui",
@@ -2773,15 +2781,6 @@ dependencies = [
"digest",
]
[[package]]
name = "home"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "http"
version = "1.3.1"
@@ -2819,7 +2818,7 @@ dependencies = [
[[package]]
name = "http_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"bytes",
@@ -2839,7 +2838,7 @@ dependencies = [
[[package]]
name = "http_client_tls"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"rustls",
"rustls-platform-verifier",
@@ -2942,16 +2941,16 @@ dependencies = [
[[package]]
name = "i18n"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"rust-i18n",
]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -2959,7 +2958,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.61.2",
"windows-core 0.62.0",
]
[[package]]
@@ -3142,9 +3141,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]]
name = "indexmap"
version = "2.11.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921"
dependencies = [
"equivalent",
"hashbrown 0.15.5",
@@ -3374,12 +3373,6 @@ dependencies = [
"spin",
]
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "leak"
version = "0.1.2"
@@ -3484,9 +3477,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "litemap"
@@ -3536,9 +3529,9 @@ dependencies = [
[[package]]
name = "lru"
version = "0.16.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ea4e65087ff52f3862caff188d489f1fab49a0cb09e01b2e3f1a617b10aaed"
checksum = "bfe949189f46fabb938b3a9a0be30fdd93fd8a09260da863399a8cf3db756ec8"
[[package]]
name = "lru-slab"
@@ -3548,9 +3541,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "lyon"
version = "1.0.1"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f"
checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352"
dependencies = [
"lyon_algorithms",
"lyon_tessellation",
@@ -3558,9 +3551,9 @@ dependencies = [
[[package]]
name = "lyon_algorithms"
version = "1.0.5"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08"
checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647"
dependencies = [
"lyon_path",
"num-traits",
@@ -3568,9 +3561,9 @@ dependencies = [
[[package]]
name = "lyon_geom"
version = "1.0.6"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570"
checksum = "ce9333c02ea4517fd31207f126124352ad59975218c114c55dbb8f9d56fd4b45"
dependencies = [
"arrayvec",
"euclid",
@@ -3579,9 +3572,9 @@ dependencies = [
[[package]]
name = "lyon_path"
version = "1.0.7"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0047f508cd7a85ad6bad9518f68cce7b1bf6b943fb71f6da0ee3bc1e8cb75f25"
checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb"
dependencies = [
"lyon_geom",
"num-traits",
@@ -3589,9 +3582,9 @@ dependencies = [
[[package]]
name = "lyon_tessellation"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c"
checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353"
dependencies = [
"float_next_after",
"lyon_path",
@@ -3636,7 +3629,7 @@ dependencies = [
[[package]]
name = "media"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"bindgen 0.71.1",
@@ -3874,17 +3867,17 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "normpath"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed"
checksum = "c178369371fd7db523726931e50d430b560e3059665abc537ba3277e9274c9c4"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.61.0",
]
[[package]]
name = "nostr"
version = "0.43.0"
source = "git+https://github.com/rust-nostr/nostr#53751050e317e1b6ab91b80b1dff4c867a519b38"
source = "git+https://github.com/rust-nostr/nostr#b1016f439ef1da5f8fdeae14368f5b7340414389"
dependencies = [
"aes",
"base64",
@@ -3908,7 +3901,7 @@ dependencies = [
[[package]]
name = "nostr-connect"
version = "0.43.0"
source = "git+https://github.com/rust-nostr/nostr#53751050e317e1b6ab91b80b1dff4c867a519b38"
source = "git+https://github.com/rust-nostr/nostr#b1016f439ef1da5f8fdeae14368f5b7340414389"
dependencies = [
"async-utility",
"nostr",
@@ -3920,7 +3913,7 @@ dependencies = [
[[package]]
name = "nostr-database"
version = "0.43.0"
source = "git+https://github.com/rust-nostr/nostr#53751050e317e1b6ab91b80b1dff4c867a519b38"
source = "git+https://github.com/rust-nostr/nostr#b1016f439ef1da5f8fdeae14368f5b7340414389"
dependencies = [
"flatbuffers",
"lru",
@@ -3931,7 +3924,7 @@ dependencies = [
[[package]]
name = "nostr-lmdb"
version = "0.43.0"
source = "git+https://github.com/rust-nostr/nostr#53751050e317e1b6ab91b80b1dff4c867a519b38"
source = "git+https://github.com/rust-nostr/nostr#b1016f439ef1da5f8fdeae14368f5b7340414389"
dependencies = [
"async-utility",
"flume",
@@ -3945,7 +3938,7 @@ dependencies = [
[[package]]
name = "nostr-relay-pool"
version = "0.43.0"
source = "git+https://github.com/rust-nostr/nostr#53751050e317e1b6ab91b80b1dff4c867a519b38"
source = "git+https://github.com/rust-nostr/nostr#b1016f439ef1da5f8fdeae14368f5b7340414389"
dependencies = [
"async-utility",
"async-wsocket",
@@ -3962,7 +3955,7 @@ dependencies = [
[[package]]
name = "nostr-sdk"
version = "0.43.0"
source = "git+https://github.com/rust-nostr/nostr#53751050e317e1b6ab91b80b1dff4c867a519b38"
source = "git+https://github.com/rust-nostr/nostr#b1016f439ef1da5f8fdeae14368f5b7340414389"
dependencies = [
"async-utility",
"nostr",
@@ -4259,12 +4252,12 @@ checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea"
[[package]]
name = "oo7"
version = "0.4.3"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb23d3ec3527d65a83be1c1795cb883c52cfa57147d42acc797127df56fc489"
checksum = "e3299dd401feaf1d45afd8fd1c0586f10fcfb22f244bb9afa942cec73503b89d"
dependencies = [
"aes",
"ashpd",
"ashpd 0.12.0",
"async-fs",
"async-io",
"async-lock",
@@ -4609,7 +4602,7 @@ dependencies = [
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix 1.0.8",
"rustix 1.1.2",
"windows-sys 0.60.2",
]
@@ -5076,7 +5069,7 @@ dependencies = [
[[package]]
name = "refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"derive_refineable",
"workspace-hack",
@@ -5113,7 +5106,7 @@ checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "registry"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"common",
@@ -5230,7 +5223,7 @@ dependencies = [
[[package]]
name = "reqwest_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"bytes",
@@ -5429,15 +5422,15 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.8"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags 2.9.4",
"errno",
"libc",
"linux-raw-sys 0.9.4",
"windows-sys 0.60.2",
"linux-raw-sys 0.11.0",
"windows-sys 0.61.0",
]
[[package]]
@@ -5465,7 +5458,7 @@ dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework 3.3.0",
"security-framework 3.4.0",
]
[[package]]
@@ -5502,7 +5495,7 @@ dependencies = [
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework 3.3.0",
"security-framework 3.4.0",
"security-framework-sys",
"webpki-root-certs 0.26.11",
"windows-sys 0.59.0",
@@ -5516,9 +5509,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.4"
version = "0.103.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8"
dependencies = [
"aws-lc-rs",
"ring",
@@ -5614,11 +5607,11 @@ dependencies = [
[[package]]
name = "schannel"
version = "0.1.27"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.61.0",
]
[[package]]
@@ -5735,9 +5728,9 @@ dependencies = [
[[package]]
name = "security-framework"
version = "3.3.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c"
checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640"
dependencies = [
"bitflags 2.9.4",
"core-foundation 0.10.1",
@@ -5748,9 +5741,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.14.0"
version = "2.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
dependencies = [
"core-foundation-sys",
"libc",
@@ -5765,7 +5758,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "semantic_version"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"serde",
@@ -5900,7 +5893,7 @@ dependencies = [
[[package]]
name = "settings"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"global",
@@ -5967,7 +5960,7 @@ dependencies = [
[[package]]
name = "signer_proxy"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"atomic-destructor",
@@ -6206,7 +6199,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "sum_tree"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"arrayvec",
"log",
@@ -6476,15 +6469,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tempfile"
version = "3.21.0"
version = "3.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53"
dependencies = [
"fastrand 2.3.0",
"getrandom 0.3.3",
"once_cell",
"rustix 1.0.8",
"windows-sys 0.60.2",
"rustix 1.1.2",
"windows-sys 0.61.0",
]
[[package]]
@@ -6509,7 +6502,7 @@ dependencies = [
[[package]]
name = "theme"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"gpui",
@@ -6671,7 +6664,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "title_bar"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"common",
@@ -7042,7 +7035,7 @@ dependencies = [
[[package]]
name = "ui"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"anyhow",
"common",
@@ -7105,9 +7098,9 @@ checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
[[package]]
name = "unicode-ident"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "unicode-linebreak"
@@ -7236,7 +7229,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "util"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#0ef7ee172fd8dd4d0638a9731140cb3a74344918"
source = "git+https://github.com/zed-industries/zed#ded646760467d653fd57ee3ef4fc1edcfafba6ae"
dependencies = [
"anyhow",
"async-fs",
@@ -7405,9 +7398,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.4+wasi-0.2.4"
version = "0.14.5+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a"
checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4"
dependencies = [
"wasip2",
]
[[package]]
name = "wasip2"
version = "1.0.0+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24"
dependencies = [
"wit-bindgen",
]
@@ -7511,7 +7513,7 @@ checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
dependencies = [
"cc",
"downcast-rs",
"rustix 1.0.8",
"rustix 1.1.2",
"scoped-tls",
"smallvec",
"wayland-sys",
@@ -7524,7 +7526,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
dependencies = [
"bitflags 2.9.4",
"rustix 1.0.8",
"rustix 1.1.2",
"wayland-backend",
"wayland-scanner",
]
@@ -7535,7 +7537,7 @@ version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29"
dependencies = [
"rustix 1.0.8",
"rustix 1.1.2",
"wayland-client",
"xcursor",
]
@@ -7666,18 +7668,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix 0.38.44",
]
[[package]]
name = "whoami"
version = "1.6.1"
@@ -7790,6 +7780,19 @@ dependencies = [
"windows-strings 0.4.2",
]
[[package]]
name = "windows-core"
version = "0.62.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-link 0.2.0",
"windows-result 0.4.0",
"windows-strings 0.5.0",
]
[[package]]
name = "windows-future"
version = "0.2.1"
@@ -7907,6 +7910,15 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-result"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
dependencies = [
"windows-link 0.2.0",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
@@ -7925,6 +7937,15 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-strings"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
dependencies = [
"windows-link 0.2.0",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
@@ -8306,7 +8327,7 @@ dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"rustix 1.0.8",
"rustix 1.1.2",
"x11rb-protocol",
"xcursor",
]
@@ -8324,7 +8345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909"
dependencies = [
"libc",
"rustix 1.0.8",
"rustix 1.1.2",
]
[[package]]
@@ -8441,9 +8462,9 @@ dependencies = [
[[package]]
name = "zbus"
version = "5.10.0"
version = "5.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be"
checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7"
dependencies = [
"async-broadcast",
"async-executor",
@@ -8474,9 +8495,9 @@ dependencies = [
[[package]]
name = "zbus_macros"
version = "5.10.0"
version = "5.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131"
checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca"
dependencies = [
"proc-macro-crate",
"proc-macro2",
+1 -1
View File
@@ -4,7 +4,7 @@ members = ["crates/*"]
default-members = ["crates/coop"]
[workspace.package]
version = "0.2.6"
version = "0.2.7"
edition = "2021"
publish = false
+1 -1
View File
@@ -14,7 +14,7 @@ product-name = "Coop"
description = "Chat Freely, Stay Private on Nostr"
identifier = "su.reya.coop"
category = "SocialNetworking"
version = "0.2.6"
version = "0.2.7"
out-dir = "../../dist"
before-packaging-command = "cargo build --release"
resources = ["Cargo.toml", "src"]
+1 -1
View File
@@ -2,7 +2,7 @@ use std::sync::Mutex;
use gpui::{actions, App};
actions!(coop, [DarkMode, Settings, Logout, Quit]);
actions!(coop, [ReloadMetadata, DarkMode, Settings, Logout, Quit]);
actions!(sidebar, [Reload, RelayStatus]);
pub fn load_embedded_fonts(cx: &App) {
+168 -137
View File
@@ -42,7 +42,7 @@ use ui::notification::Notification;
use ui::popup_menu::PopupMenuExt;
use ui::{h_flex, v_flex, ContextModal, Disableable, IconName, Root, Sizable, StyledExt};
use crate::actions::{DarkMode, Logout, Settings};
use crate::actions::{DarkMode, Logout, ReloadMetadata, Settings};
use crate::views::compose::compose_button;
use crate::views::setup_relay::setup_nip17_relay;
use crate::views::{
@@ -64,18 +64,22 @@ pub fn new_account(window: &mut Window, cx: &mut App) {
}
pub struct ChatSpace {
// Workspace
// App's Title Bar
title_bar: Entity<TitleBar>,
// App's Dock Area
dock: Entity<DockArea>,
// Temporarily store all authentication requests
auth_requests: HashMap<AuthRequest, bool>,
// All authentication requests
auth_requests: HashMap<RelayUrl, AuthRequest>,
// Local state to determine if the user has set up NIP-17 relays
has_nip17_relays: bool,
nip17_relays: bool,
// System
// All subscriptions for observing the app state
_subscriptions: SmallVec<[Subscription; 3]>,
// All long running tasks
_tasks: SmallVec<[Task<()>; 5]>,
}
@@ -182,7 +186,7 @@ impl ChatSpace {
dock,
title_bar,
auth_requests: HashMap::new(),
has_nip17_relays: true,
nip17_relays: true,
_subscriptions: subscriptions,
_tasks: tasks,
}
@@ -212,67 +216,60 @@ impl ChatSpace {
async fn observe_signer() {
let client = nostr_client();
let ingester = ingester();
let stream_timeout = Duration::from_secs(5);
let loop_duration = Duration::from_secs(1);
let mut is_sent_signal = false;
let mut identity: Option<PublicKey> = None;
loop {
if let Some(public_key) = identity {
let nip65 = Filter::new().kind(Kind::RelayList).author(public_key);
let Ok(signer) = client.signer().await else {
smol::Timer::after(loop_duration).await;
continue;
};
if client.database().count(nip65).await.unwrap_or(0) > 0 {
let dm_relays = Filter::new().kind(Kind::InboxRelays).author(public_key);
let Ok(public_key) = signer.get_public_key().await else {
smol::Timer::after(loop_duration).await;
continue;
};
match client.database().query(dm_relays).await {
Ok(events) => {
if let Some(event) = events.first_owned() {
let relay_urls = nip17::extract_relay_list(&event).collect_vec();
// Notify the app that the signer has been set.
ingester.send(Signal::SignerSet(public_key)).await;
if relay_urls.is_empty() {
if !is_sent_signal {
ingester.send(Signal::DmRelayNotFound).await;
is_sent_signal = true;
}
} else {
break;
}
} else if !is_sent_signal {
ingester.send(Signal::DmRelayNotFound).await;
is_sent_signal = true;
} else {
break;
}
}
Err(e) => {
log::error!("Database query error: {e}");
if !is_sent_signal {
ingester.send(Signal::DmRelayNotFound).await;
is_sent_signal = true;
}
}
}
} else {
log::error!("Database error.");
break;
}
} else {
// Wait for signer set
if let Ok(signer) = client.signer().await {
if let Ok(public_key) = signer.get_public_key().await {
identity = Some(public_key);
// Subscribe to the NIP-65 relays for the public key.
let filter = Filter::new()
.kind(Kind::RelayList)
.author(public_key)
.limit(1);
// Notify the app that the signer has been set.
ingester.send(Signal::SignerSet(public_key)).await;
match client
.stream_events_from(BOOTSTRAP_RELAYS, filter, stream_timeout)
.await
{
Ok(mut stream) => {
let mut processed_ids = HashSet::new();
// Subscribe to the NIP-65 relays for the public key.
if let Err(e) = Self::fetch_nip65_relays(public_key).await {
log::error!("Failed to fetch NIP-65 relays: {e}");
if let Some(event) = stream.next().await {
if processed_ids.insert(event.id) {
// Fetch user's metadata event
Self::fetch_single_event(Kind::Metadata, event.pubkey).await;
// Fetch user's contact list event
Self::fetch_single_event(Kind::ContactList, event.pubkey).await;
// Fetch user's inbox relays event
Self::fetch_nip17_relays(event.pubkey).await;
break;
}
} else {
ingester.send(Signal::DmRelayNotFound).await;
}
}
}
Err(e) => {
log::error!("Error fetching NIP-17 Relay: {e:?}");
ingester.send(Signal::DmRelayNotFound).await;
}
};
smol::Timer::after(loop_duration).await;
break;
}
}
@@ -391,39 +388,6 @@ impl ChatSpace {
}
match event.kind {
Kind::RelayList => {
if let Ok(true) = Self::is_self_event(&event).await {
// Fetch user's metadata event
Self::fetch_single_event(Kind::Metadata, event.pubkey).await;
// Fetch user's contact list event
Self::fetch_single_event(Kind::ContactList, event.pubkey).await;
// Fetch user's inbox relays event
Self::fetch_single_event(Kind::InboxRelays, event.pubkey).await;
}
}
Kind::InboxRelays => {
if let Ok(true) = Self::is_self_event(&event).await {
let relays = nip17::extract_relay_list(&event).collect_vec();
if !relays.is_empty() {
for relay in relays.clone().into_iter() {
if client.add_relay(relay).await.is_err() {
let notice = Notice::RelayFailed(relay.clone());
ingester.send(Signal::Notice(notice)).await;
}
if client.connect_relay(relay).await.is_err() {
let notice = Notice::RelayFailed(relay.clone());
ingester.send(Signal::Notice(notice)).await;
}
}
// Subscribe to gift wrap events only in the current user's NIP-17 relays
Self::fetch_gift_wrap(relays, event.pubkey).await;
}
}
}
Kind::ContactList => {
if let Ok(true) = Self::is_self_event(&event).await {
let public_keys = event.tags.public_keys().copied().collect_vec();
@@ -439,7 +403,10 @@ impl ChatSpace {
}
}
Kind::Metadata => {
ingester.send(Signal::Metadata(event.into_owned())).await;
if let Ok(metadata) = Metadata::from_json(&event.content) {
let profile = Profile::new(event.pubkey, metadata);
ingester.send(Signal::Metadata(profile)).await;
}
}
Kind::GiftWrap => {
Self::unwrap_gift_wrap(&event, pubkey_tx).await;
@@ -558,9 +525,9 @@ impl ChatSpace {
this.set_unwrapping_status(status, cx);
});
}
Signal::Metadata(event) => {
Signal::Metadata(profile) => {
registry.update(cx, |this, cx| {
this.insert_or_update_person(event, cx);
this.insert_or_update_person(profile, cx);
});
}
Signal::Message((gift_wrap_id, event)) => {
@@ -570,7 +537,7 @@ impl ChatSpace {
}
Signal::DmRelayNotFound => {
view.update(cx, |this, cx| {
this.set_no_nip17_relays(cx);
this.set_required_relays(cx);
})
.ok();
}
@@ -603,6 +570,49 @@ impl ChatSpace {
}
}
pub async fn fetch_nip17_relays(public_key: PublicKey) {
let client = nostr_client();
let ingester = ingester();
let filter = Filter::new()
.kind(Kind::InboxRelays)
.author(public_key)
.limit(1);
match client.stream_events(filter, Duration::from_secs(5)).await {
Ok(mut stream) => {
let mut processed_ids = HashSet::new();
if let Some(event) = stream.next().await {
if processed_ids.insert(event.id) {
let relays = nip17::extract_relay_list(&event).collect_vec();
if !relays.is_empty() {
for relay in relays.clone().into_iter() {
if client.add_relay(relay).await.is_err() {
let notice = Notice::RelayFailed(relay.clone());
ingester.send(Signal::Notice(notice)).await;
}
if client.connect_relay(relay).await.is_err() {
let notice = Notice::RelayFailed(relay.clone());
ingester.send(Signal::Notice(notice)).await;
}
}
// Subscribe to gift wrap events only in the current user's NIP-17 relays
Self::fetch_gift_wrap(relays, event.pubkey).await;
}
}
} else {
ingester.send(Signal::DmRelayNotFound).await;
}
}
Err(e) => {
log::error!("Error fetching NIP-17 Relay: {e:?}");
ingester.send(Signal::DmRelayNotFound).await;
}
};
}
pub async fn fetch_gift_wrap(relays: Vec<&RelayUrl>, public_key: PublicKey) {
let client = nostr_client();
let sub_id = css().gift_wrap_sub_id.clone();
@@ -617,23 +627,6 @@ impl ChatSpace {
}
}
/// Fetches NIP-65 relay list for a given public key
pub async fn fetch_nip65_relays(public_key: PublicKey) -> Result<(), Error> {
let client = nostr_client();
let css = css();
let filter = Filter::new()
.kind(Kind::RelayList)
.author(public_key)
.limit(1);
client
.subscribe_to(BOOTSTRAP_RELAYS, filter, css.auto_close_opts)
.await?;
Ok(())
}
/// Fetches metadata for a list of public keys
async fn fetch_metadata_for_pubkeys(public_keys: HashSet<PublicKey>) {
if public_keys.is_empty() {
@@ -725,10 +718,8 @@ impl ChatSpace {
match event.created_at >= css.init_at {
// New message: send a signal to notify the UI
true => {
// Prevent notification if the event was sent by Coop
if !css.sent_ids.read().await.contains(&target.id) {
ingester.send(Signal::Message((target.id, event))).await;
}
smol::Timer::after(Duration::from_millis(200)).await;
ingester.send(Signal::Message((target.id, event))).await;
}
// Old message: Coop is probably processing the user's messages during initial load
false => {
@@ -939,20 +930,20 @@ impl ChatSpace {
}
fn reopen_auth_request(&mut self, window: &mut Window, cx: &mut Context<Self>) {
for req in self.auth_requests.clone().into_iter() {
self.open_auth_request(req.0, window, cx);
for (_, request) in self.auth_requests.clone().into_iter() {
self.open_auth_request(request, window, cx);
}
}
fn push_auth_request(&mut self, req: &AuthRequest, cx: &mut Context<Self>) {
self.auth_requests.insert(req.to_owned(), false);
self.auth_requests.insert(req.url.clone(), req.to_owned());
cx.notify();
}
fn sending_auth_request(&mut self, challenge: &str, cx: &mut Context<Self>) {
for (req, status) in self.auth_requests.iter_mut() {
for (_, req) in self.auth_requests.iter_mut() {
if req.challenge == challenge {
*status = true;
req.sending = true;
cx.notify();
}
}
@@ -962,16 +953,16 @@ impl ChatSpace {
if let Some(req) = self
.auth_requests
.iter()
.find(|(req, _)| req.challenge == challenge)
.find(|(_, req)| req.challenge == challenge)
{
req.1.to_owned()
req.1.sending
} else {
false
}
}
fn remove_auth_request(&mut self, challenge: &str, cx: &mut Context<Self>) {
self.auth_requests.retain(|r, _| r.challenge != challenge);
self.auth_requests.retain(|_, r| r.challenge != challenge);
cx.notify();
}
@@ -1023,8 +1014,8 @@ impl ChatSpace {
});
}
fn set_no_nip17_relays(&mut self, cx: &mut Context<Self>) {
self.has_nip17_relays = false;
fn set_required_relays(&mut self, cx: &mut Context<Self>) {
self.nip17_relays = false;
cx.notify();
}
@@ -1051,19 +1042,13 @@ impl ChatSpace {
cx.spawn_in(window, async move |this, cx| {
if let Ok((secret, profile)) = task.await {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.set_account_layout(secret, profile, window, cx);
})
.ok();
this.update_in(cx, |this, window, cx| {
this.set_account_layout(secret, profile, window, cx);
})
.ok();
} else {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.set_onboarding_layout(window, cx);
})
.ok();
this.update_in(cx, |this, window, cx| {
this.set_onboarding_layout(window, cx);
})
.ok();
}
@@ -1090,6 +1075,50 @@ impl ChatSpace {
}
}
fn on_reload_metadata(
&mut self,
_ev: &ReloadMetadata,
window: &mut Window,
cx: &mut Context<Self>,
) {
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let client = nostr_client();
let css = css();
let filter = Filter::new().kind(Kind::PrivateDirectMessage);
let pubkeys: Vec<PublicKey> = client
.database()
.query(filter)
.await?
.into_iter()
.flat_map(|event| event.all_pubkeys())
.unique()
.collect();
let filter = Filter::new()
.kind(Kind::Metadata)
.limit(pubkeys.len())
.authors(pubkeys);
client
.subscribe_to(BOOTSTRAP_RELAYS, filter, css.auto_close_opts)
.await?;
Ok(())
});
cx.spawn_in(window, async move |_, cx| {
if task.await.is_ok() {
cx.update(|window, cx| {
window.push_notification(t!("common.refreshed"), cx);
})
.ok();
}
})
.detach();
}
fn on_sign_out(&mut self, _e: &Logout, _window: &mut Window, cx: &mut Context<Self>) {
cx.background_spawn(async move {
let client = nostr_client();
@@ -1236,7 +1265,6 @@ impl ChatSpace {
cx: &mut Context<Self>,
) -> impl IntoElement {
let proxy = AppSettings::get_proxy_user_avatars(cx);
let is_auto_auth = AppSettings::read_global(cx).is_auto_auth();
let updating = AutoUpdater::read_global(cx).status.is_updating();
let updated = AutoUpdater::read_global(cx).status.is_updated();
let auth_requests = self.auth_requests.len();
@@ -1275,7 +1303,7 @@ impl ChatSpace {
}),
)
})
.when(auth_requests > 0 && !is_auto_auth, |this| {
.when(auth_requests > 0, |this| {
this.child(
h_flex()
.id("requests")
@@ -1295,7 +1323,7 @@ impl ChatSpace {
})),
)
})
.when(!self.has_nip17_relays, |this| {
.when(!self.nip17_relays, |this| {
this.child(setup_nip17_relay(t!("relays.button")))
})
.child(
@@ -1309,6 +1337,8 @@ impl ChatSpace {
this.menu(t!("user.dark_mode"), Box::new(DarkMode))
.menu(t!("user.settings"), Box::new(Settings))
.separator()
.menu(t!("user.reload_metadata"), Box::new(ReloadMetadata))
.separator()
.menu(t!("user.sign_out"), Box::new(Logout))
}),
)
@@ -1429,6 +1459,7 @@ impl Render for ChatSpace {
.on_action(cx.listener(Self::on_dark_mode))
.on_action(cx.listener(Self::on_sign_out))
.on_action(cx.listener(Self::on_open_profile))
.on_action(cx.listener(Self::on_reload_metadata))
.relative()
.size_full()
.child(
+99 -54
View File
@@ -24,7 +24,7 @@ use smallvec::{smallvec, SmallVec};
use smol::fs;
use theme::ActiveTheme;
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants};
use ui::button::{Button, ButtonRounded, ButtonVariants};
use ui::dock_area::panel::{Panel, PanelEvent};
use ui::emoji_picker::EmojiPicker;
use ui::input::{InputEvent, InputState, TextInput};
@@ -307,6 +307,38 @@ impl Chat {
.detach();
}
fn resend_message(&mut self, id: &EventId, window: &mut Window, cx: &mut Context<Self>) {
if let Some(reports) = self.reports_by_id.get(id).cloned() {
if let Some(message) = self.message(id) {
let backup = AppSettings::get_backup_messages(cx);
let id_clone = id.to_owned();
let message = message.content.to_owned();
let task = self.room.read(cx).resend(reports, message, backup, cx);
cx.spawn_in(window, async move |this, cx| {
match task.await {
Ok(reports) => {
this.update(cx, |this, cx| {
this.reports_by_id.entry(id_clone).and_modify(|this| {
*this = reports;
});
cx.notify();
})
.ok();
}
Err(e) => {
cx.update(|window, cx| {
window.push_notification(e.to_string(), cx);
})
.ok();
}
};
})
.detach();
}
}
}
/// Check if a message failed to send by its ID
fn is_sent_failed(&self, id: &EventId) -> bool {
self.reports_by_id
@@ -609,7 +641,23 @@ impl Chat {
})
.child(text)
.when(is_sent_failed, |this| {
this.child(self.render_message_reports(&id, cx))
this.child(
h_flex()
.gap_1()
.child(self.render_message_reports(&id, cx))
.child(
Button::new(SharedString::from(id.to_hex()))
.label(t!("common.resend"))
.danger()
.xsmall()
.rounded(ButtonRounded::Full)
.on_click(cx.listener(
move |this, _, window, cx| {
this.resend_message(&id, window, cx);
},
)),
),
)
}),
),
)
@@ -677,15 +725,17 @@ impl Chat {
}
fn render_message_sent(&self, id: &EventId, _cx: &Context<Self>) -> impl IntoElement {
div().id("").child(shared_t!("chat.sent")).when_some(
self.sent_reports(id).cloned(),
|this, reports| {
div()
.id(SharedString::from(id.to_hex()))
.child(shared_t!("chat.sent"))
.when_some(self.sent_reports(id).cloned(), |this, reports| {
this.on_click(move |_e, window, cx| {
let reports = reports.clone();
window.open_modal(cx, move |this, _window, cx| {
this.title(shared_t!("chat.reports")).child(
v_flex().pb_4().gap_4().children({
this.show_close(true)
.title(shared_t!("chat.reports"))
.child(v_flex().pb_4().gap_4().children({
let mut items = Vec::with_capacity(reports.len());
for report in reports.iter() {
@@ -693,30 +743,29 @@ impl Chat {
}
items
}),
)
}))
});
})
},
)
})
}
fn render_message_reports(&self, id: &EventId, cx: &Context<Self>) -> impl IntoElement {
h_flex()
.id("")
.gap_1()
.id(SharedString::from(id.to_hex()))
.gap_0p5()
.text_color(cx.theme().danger_foreground)
.text_xs()
.italic()
.child(Icon::new(IconName::Info).small())
.child(Icon::new(IconName::Info).xsmall())
.child(shared_t!("chat.sent_failed"))
.when_some(self.sent_reports(id).cloned(), |this, reports| {
this.on_click(move |_e, window, cx| {
let reports = reports.clone();
window.open_modal(cx, move |this, _window, cx| {
this.title(shared_t!("chat.reports")).child(
v_flex().pb_4().gap_4().children({
this.show_close(true)
.title(shared_t!("chat.reports"))
.child(v_flex().gap_4().pb_4().w_full().children({
let mut items = Vec::with_capacity(reports.len());
for report in reports.iter() {
@@ -724,8 +773,7 @@ impl Chat {
}
items
}),
)
}))
});
})
})
@@ -739,6 +787,7 @@ impl Chat {
v_flex()
.gap_2()
.w_full()
.child(
h_flex()
.gap_2()
@@ -752,7 +801,7 @@ impl Chat {
.child(name.clone()),
),
)
.when(report.nip17_relays_not_found, |this| {
.when(report.relays_not_found, |this| {
this.child(
h_flex()
.flex_wrap()
@@ -773,7 +822,7 @@ impl Chat {
),
)
})
.when_some(report.local_error.clone(), |this, error| {
.when_some(report.error.clone(), |this, error| {
this.child(
h_flex()
.flex_wrap()
@@ -788,38 +837,36 @@ impl Chat {
.child(div().flex_1().w_full().text_center().child(error)),
)
})
.when_some(report.output.clone(), |this, output| {
.when_some(report.status.clone(), |this, output| {
this.child(
v_flex()
.gap_2()
.text_xs()
.w_full()
.children({
let mut items = Vec::with_capacity(output.failed.len());
for (url, msg) in output.failed.into_iter() {
items.push(
h_flex()
.gap_1()
.justify_between()
.text_sm()
v_flex()
.gap_0p5()
.py_1()
.px_2()
.w_full()
.rounded(cx.theme().radius)
.bg(cx.theme().elevated_surface_background)
.child(
div()
.flex_1()
.py_0p5()
.px_2()
.bg(cx.theme().elevated_surface_background)
.rounded_sm()
.child(url.to_string()),
.text_xs()
.font_semibold()
.line_height(relative(1.25))
.child(SharedString::from(url.to_string())),
)
.child(
div()
.flex_1()
.py_0p5()
.px_2()
.bg(cx.theme().danger_background)
.text_sm()
.text_color(cx.theme().danger_foreground)
.rounded_sm()
.child(msg.to_string()),
.line_height(relative(1.25))
.child(SharedString::from(msg.to_string())),
),
)
}
@@ -831,27 +878,25 @@ impl Chat {
for url in output.success.into_iter() {
items.push(
h_flex()
.gap_1()
.justify_between()
.text_sm()
v_flex()
.gap_0p5()
.py_1()
.px_2()
.w_full()
.rounded(cx.theme().radius)
.bg(cx.theme().elevated_surface_background)
.child(
div()
.flex_1()
.py_0p5()
.px_2()
.bg(cx.theme().elevated_surface_background)
.rounded_sm()
.child(url.to_string()),
.text_xs()
.font_semibold()
.line_height(relative(1.25))
.child(SharedString::from(url.to_string())),
)
.child(
div()
.flex_1()
.py_0p5()
.px_2()
.bg(cx.theme().secondary_background)
.text_sm()
.text_color(cx.theme().secondary_foreground)
.rounded_sm()
.line_height(relative(1.25))
.child(shared_t!("chat.sent_success")),
),
)
@@ -921,7 +966,7 @@ impl Chat {
let path: SharedString = url.to_string().into();
div()
.id("")
.id(SharedString::from(url.to_string()))
.relative()
.w_16()
.child(
+9 -2
View File
@@ -164,7 +164,7 @@ impl EditProfile {
.detach();
}
pub fn set_metadata(&mut self, cx: &mut Context<Self>) -> Task<Result<Option<Event>, Error>> {
pub fn set_metadata(&mut self, cx: &mut Context<Self>) -> Task<Result<Option<Profile>, Error>> {
let avatar = self.avatar_input.read(cx).value().to_string();
let name = self.name_input.read(cx).value().to_string();
let bio = self.bio_input.read(cx).value().to_string();
@@ -189,7 +189,14 @@ impl EditProfile {
cx.background_spawn(async move {
let client = nostr_client();
let output = client.set_metadata(&new_metadata).await?;
let event = client.database().event_by_id(&output.val).await?;
let event = client
.database()
.event_by_id(&output.val)
.await?
.map(|event| {
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
Profile::new(event.pubkey, metadata)
});
Ok(event)
})
+6 -6
View File
@@ -41,28 +41,28 @@ impl Preferences {
fn open_edit_profile(&self, window: &mut Window, cx: &mut Context<Self>) {
let view = edit_profile::init(window, cx);
let weak_view = view.downgrade();
let title = SharedString::new(t!("profile.title"));
window.open_modal(cx, move |modal, _window, _cx| {
let weak_view = weak_view.clone();
modal
.confirm()
.title(title.clone())
.title(shared_t!("profile.title"))
.child(view.clone())
.button_props(ModalButtonProps::default().ok_text(t!("common.update")))
.on_ok(move |_, window, cx| {
weak_view
.update(cx, |this, cx| {
let set_metadata = this.set_metadata(cx);
let registry = Registry::global(cx);
cx.spawn_in(window, async move |_, cx| {
match set_metadata.await {
Ok(event) => {
if let Some(event) = event {
Ok(profile) => {
if let Some(profile) = profile {
cx.update(|_, cx| {
Registry::global(cx).update(cx, |this, cx| {
this.insert_or_update_person(event, cx);
registry.update(cx, |this, cx| {
this.insert_or_update_person(profile, cx);
});
})
.ok();
+1 -1
View File
@@ -37,7 +37,7 @@ pub const NOSTR_CONNECT_RELAY: &str = "wss://relay.nsec.app";
pub const RELAY_RETRY: u64 = 2;
/// Default retry count for sending messages
pub const SEND_RETRY: u64 = 5;
pub const SEND_RETRY: u64 = 10;
/// Default timeout (in seconds) for Nostr Connect
pub const NOSTR_CONNECT_TIMEOUT: u64 = 200;
+6 -4
View File
@@ -13,16 +13,18 @@ use crate::paths::support_dir;
pub mod constants;
pub mod paths;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct AuthRequest {
pub challenge: String,
pub url: RelayUrl,
pub challenge: String,
pub sending: bool,
}
impl AuthRequest {
pub fn new(challenge: impl Into<String>, url: RelayUrl) -> Self {
Self {
challenge: challenge.into(),
sending: false,
url,
}
}
@@ -68,8 +70,8 @@ pub enum Signal {
/// A signal to notify UI that the browser proxy service is down
ProxyDown,
/// A signal to notify UI that a new metadata event has been received
Metadata(Event),
/// A signal to notify UI that a new profile has been received
Metadata(Profile),
/// A signal to notify UI that a new gift wrap event has been received
Message((EventId, Event)),
+12 -14
View File
@@ -155,21 +155,19 @@ impl Registry {
}
/// Insert or update a person
pub fn insert_or_update_person(&mut self, event: Event, cx: &mut App) {
let public_key = event.pubkey;
let Ok(metadata) = Metadata::from_json(event.content) else {
// Invalid metadata, no need to process further.
return;
};
pub fn insert_or_update_person(&mut self, profile: Profile, cx: &mut App) {
let public_key = profile.public_key();
if let Some(person) = self.persons.get(&public_key) {
person.update(cx, |this, cx| {
*this = Profile::new(public_key, metadata);
cx.notify();
});
} else {
self.persons
.insert(public_key, cx.new(|_| Profile::new(public_key, metadata)));
match self.persons.get(&public_key) {
Some(person) => {
person.update(cx, |this, cx| {
*this = profile;
cx.notify();
});
}
None => {
self.persons.insert(public_key, cx.new(|_| profile));
}
}
}
+126 -66
View File
@@ -1,4 +1,5 @@
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::time::Duration;
@@ -16,45 +17,51 @@ use crate::Registry;
#[derive(Debug, Clone)]
pub struct SendReport {
pub receiver: PublicKey,
pub output: Option<Output<EventId>>,
pub local_error: Option<SharedString>,
pub nip17_relays_not_found: bool,
pub tags: Option<Vec<Tag>>,
pub status: Option<Output<EventId>>,
pub error: Option<SharedString>,
pub relays_not_found: bool,
}
impl SendReport {
pub fn output(receiver: PublicKey, output: Output<EventId>) -> Self {
pub fn new(receiver: PublicKey) -> Self {
Self {
receiver,
output: Some(output),
local_error: None,
nip17_relays_not_found: false,
status: None,
error: None,
tags: None,
relays_not_found: false,
}
}
pub fn error(receiver: PublicKey, error: impl Into<SharedString>) -> Self {
Self {
receiver,
output: None,
local_error: Some(error.into()),
nip17_relays_not_found: false,
}
pub fn not_found(mut self) -> Self {
self.relays_not_found = true;
self
}
pub fn nip17_relays_not_found(receiver: PublicKey) -> Self {
Self {
receiver,
output: None,
local_error: None,
nip17_relays_not_found: true,
}
pub fn error(mut self, error: impl Into<SharedString>) -> Self {
self.error = Some(error.into());
self.relays_not_found = false;
self
}
pub fn status(mut self, output: Output<EventId>) -> Self {
self.status = Some(output);
self.relays_not_found = false;
self
}
pub fn tags(mut self, tags: &Vec<Tag>) -> Self {
self.tags = Some(tags.to_owned());
self
}
pub fn is_relay_error(&self) -> bool {
self.local_error.is_some() || self.nip17_relays_not_found
self.error.is_some() || self.relays_not_found
}
pub fn is_sent_success(&self) -> bool {
if let Some(output) = self.output.as_ref() {
if let Some(output) = self.status.as_ref() {
!output.success.is_empty()
} else {
false
@@ -338,19 +345,21 @@ impl Room {
cx.background_spawn(async move {
let client = nostr_client();
let public_key = members[members.len() - 1];
let filter = Filter::new()
let sent = Filter::new()
.kind(Kind::PrivateDirectMessage)
.authors(members.clone())
.author(public_key)
.pubkeys(members.clone());
let events: Vec<Event> = client
.database()
.query(filter)
.await?
.into_iter()
.filter(|ev| ev.compare_pubkeys(&members))
.collect();
let recv = Filter::new()
.kind(Kind::PrivateDirectMessage)
.authors(members)
.pubkey(public_key);
let sent_events = client.database().query(sent).await?;
let recv_events = client.database().query(recv).await?;
let events: Vec<Event> = sent_events.merge(recv_events).into_iter().collect();
Ok(events)
})
@@ -398,17 +407,7 @@ impl Room {
event
}
/// Sends a message to all members in the background task
///
/// # Arguments
///
/// * `content` - The content of the message to send
/// * `cx` - The App context
///
/// # Returns
///
/// A Task that resolves to Result<Vec<String>, Error> where the
/// strings contain error messages for any failed sends
/// Create a task to sends a message to all members in the background
pub fn send_in_background(
&self,
content: &str,
@@ -422,20 +421,21 @@ impl Room {
let mut public_keys = self.members.clone();
cx.background_spawn(async move {
let css = css();
let client = nostr_client();
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let mut tags = public_keys
let mut tags: Vec<Tag> = public_keys
.iter()
.filter_map(|pubkey| {
if pubkey != &public_key {
Some(Tag::public_key(*pubkey))
.filter_map(|&this| {
if this != public_key {
Some(Tag::public_key(this))
} else {
None
}
})
.collect_vec();
.collect();
// Add event reference if it's present (replying to another event)
if replies.len() == 1 {
@@ -468,44 +468,50 @@ impl Room {
// Stored all send errors
let mut reports = vec![];
for receiver in public_keys.into_iter() {
for pubkey in public_keys.into_iter() {
match client
.send_private_msg(receiver, &content, tags.clone())
.send_private_msg(pubkey, &content, tags.clone())
.await
{
Ok(output) => {
if output
.failed
.iter()
.any(|(_, msg)| msg.starts_with("auth-required:"))
{
let id = output.id();
let id = output.id().to_owned();
let auth_required = output.failed.iter().any(|m| m.1.starts_with("auth-"));
let report = SendReport::new(pubkey).status(output).tags(&tags);
if auth_required {
// Wait for authenticated and resent event successfully
for attempt in 0..=SEND_RETRY {
// Check if event was successfully resent
if let Some(output) =
css().resent_ids.read().await.iter().find(|o| o.id() == id)
if let Some(output) = css
.resent_ids
.read()
.await
.iter()
.find(|e| e.id() == &id)
.cloned()
{
reports.push(SendReport::output(receiver, output.to_owned()));
let output = SendReport::new(pubkey).status(output).tags(&tags);
reports.push(output);
break;
}
// Check if retry limit exceeded
if attempt == SEND_RETRY {
reports.push(report);
break;
}
smol::Timer::after(Duration::from_secs(1)).await;
smol::Timer::after(Duration::from_millis(1200)).await;
}
} else {
reports.push(SendReport::output(receiver, output));
reports.push(report);
}
}
Err(e) => {
if let nostr_sdk::client::Error::PrivateMsgRelaysNotFound = e {
reports.push(SendReport::nip17_relays_not_found(receiver));
reports.push(SendReport::new(pubkey).not_found().tags(&tags));
} else {
reports.push(SendReport::error(receiver, e.to_string()));
reports.push(SendReport::new(pubkey).error(e.to_string()).tags(&tags));
}
}
}
@@ -518,13 +524,14 @@ impl Room {
.await
{
Ok(output) => {
reports.push(SendReport::output(public_key, output));
reports.push(SendReport::new(public_key).status(output).tags(&tags));
}
Err(e) => {
if let nostr_sdk::client::Error::PrivateMsgRelaysNotFound = e {
reports.push(SendReport::nip17_relays_not_found(public_key));
reports.push(SendReport::new(public_key).not_found());
} else {
reports.push(SendReport::error(public_key, e.to_string()));
reports
.push(SendReport::new(public_key).error(e.to_string()).tags(&tags));
}
}
}
@@ -533,4 +540,57 @@ impl Room {
Ok(reports)
})
}
/// Create a task to resend a failed message
pub fn resend(
&self,
reports: Vec<SendReport>,
message: String,
backup: bool,
cx: &App,
) -> Task<Result<Vec<SendReport>, Error>> {
cx.background_spawn(async move {
let client = nostr_client();
let mut resend_reports = vec![];
let mut resend_tag = vec![];
for report in reports.into_iter() {
if let Some(output) = report.status {
let id = output.id();
let urls: Vec<&RelayUrl> = output.failed.keys().collect();
if let Some(event) = client.database().event_by_id(id).await? {
for url in urls.into_iter() {
let relay = client.pool().relay(url).await?;
let id = relay.send_event(&event).await?;
let resent: Output<EventId> = Output {
val: id,
success: HashSet::from([url.to_owned()]),
failed: HashMap::new(),
};
resend_reports.push(SendReport::new(report.receiver).status(resent));
}
if let Some(tags) = report.tags {
resend_tag.extend(tags);
}
}
}
}
// Only send a backup message to current user if sent successfully to others
if backup && !resend_reports.is_empty() {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let output = client
.send_private_msg(public_key, message, resend_tag)
.await?;
resend_reports.push(SendReport::new(public_key).status(output));
}
Ok(resend_reports)
})
}
}
+5 -1
View File
@@ -49,6 +49,8 @@ common:
en: "Relay URL is not valid."
recommended:
en: "Recommended:"
resend:
en: "Resend"
auto_update:
updating:
@@ -58,9 +60,11 @@ auto_update:
user:
dark_mode:
en: "Dark Mode"
en: "Dark mode"
settings:
en: "Settings"
reload_metadata:
en: "Reload metadata"
sign_out:
en: "Sign out"