diff --git a/Cargo.lock b/Cargo.lock index f430e0b..aa19728 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,25 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "account" -version = "0.3.0" -dependencies = [ - "anyhow", - "common", - "gpui", - "log", - "nostr-sdk", - "serde", - "serde_json", - "settings", - "smallvec", - "smol", - "state", - "theme", - "ui", -] - [[package]] name = "adler2" version = "2.0.1" @@ -133,7 +114,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -185,9 +166,9 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +checksum = "618a409b91d5265798a99e3d1d0b226911605e581c4e7255e83c1e397b172bce" dependencies = [ "async-fs", "async-net", @@ -204,24 +185,6 @@ 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.3.0" @@ -269,13 +232,12 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" dependencies = [ "compression-codecs", "compression-core", - "futures-core", "futures-io", "pin-project-lite", ] @@ -386,7 +348,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -462,7 +424,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -624,9 +586,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bech32" @@ -651,7 +613,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -730,8 +692,7 @@ dependencies = [ [[package]] name = "blade-graphics" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4deb8f595ce7f00dee3543ebf6fd9a20ea86fc421ab79600dac30876250bdae" +source = "git+https://github.com/kvark/blade?rev=e3cf011ca18a6dfd907d1dedd93e85e21f005fe3#e3cf011ca18a6dfd907d1dedd93e85e21f005fe3" dependencies = [ "ash", "ash-window", @@ -765,19 +726,17 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27142319e2f4c264581067eaccb9f80acccdde60d8b4bf57cc50cd3152f109ca" +source = "git+https://github.com/kvark/blade?rev=e3cf011ca18a6dfd907d1dedd93e85e21f005fe3#e3cf011ca18a6dfd907d1dedd93e85e21f005fe3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "blade-util" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6be3a82c001ba7a17b6f8e413ede5d1004e6047213f8efaf0ffc15b5c4904c" +source = "git+https://github.com/kvark/blade?rev=e3cf011ca18a6dfd907d1dedd93e85e21f005fe3#e3cf011ca18a6dfd907d1dedd93e85e21f005fe3" dependencies = [ "blade-graphics", "bytemuck", @@ -876,7 +835,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -944,16 +903,16 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.111", + "syn 2.0.114", "tempfile", "toml 0.8.23", ] [[package]] name = "cc" -version = "1.2.50" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "jobserver", @@ -1025,10 +984,9 @@ dependencies = [ name = "chat" version = "0.3.0" dependencies = [ - "account", "anyhow", "common", - "encryption", + "device", "flume", "futures", "fuzzy-matcher", @@ -1049,12 +1007,10 @@ dependencies = [ name = "chat_ui" version = "0.3.0" dependencies = [ - "account", "anyhow", "chat", "common", "emojis", - "encryption", "gpui", "gpui_tokio", "indexset", @@ -1199,7 +1155,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1253,9 +1209,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.35" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" +checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" dependencies = [ "compression-core", "deflate64", @@ -1293,7 +1249,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "tiny-keccak", ] @@ -1308,15 +1264,13 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" name = "coop" version = "0.3.0" dependencies = [ - "account", "anyhow", "assets", "auto_update", "chat", "chat_ui", "common", - "encryption", - "encryption_ui", + "device", "futures", "gpui", "gpui_tokio", @@ -1597,9 +1551,9 @@ checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "data-url" @@ -1623,17 +1577,35 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", +] + +[[package]] +name = "device" +version = "0.3.0" +dependencies = [ + "anyhow", + "common", + "flume", + "gpui", + "itertools 0.13.0", + "log", + "nostr-sdk", + "serde", + "serde_json", + "smallvec", + "smol", + "state", ] [[package]] @@ -1712,7 +1684,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -1802,7 +1774,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "vswhom", "winreg", ] @@ -1825,49 +1797,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "encryption" -version = "0.3.0" -dependencies = [ - "account", - "anyhow", - "common", - "flume", - "futures", - "gpui", - "log", - "nostr-sdk", - "serde", - "serde_json", - "smallvec", - "smol", - "state", -] - -[[package]] -name = "encryption_ui" -version = "0.3.0" -dependencies = [ - "account", - "anyhow", - "common", - "encryption", - "futures", - "gpui", - "itertools 0.13.0", - "log", - "nostr-sdk", - "person", - "serde", - "serde_json", - "settings", - "smallvec", - "smol", - "state", - "theme", - "ui", -] - [[package]] name = "endi" version = "1.1.1" @@ -1892,7 +1821,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -1912,7 +1841,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2035,7 +1964,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2081,9 +2010,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "flatbuffers" @@ -2097,9 +2026,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide", @@ -2229,7 +2158,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2282,9 +2211,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "ftree" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae0379499242d3b9355c5069b43b9417def8c9b09903b930db1fe49318dc9e9" +checksum = "eeef4a9366aaf0ed0bb5292b7c489d80600a7431fca0d96271e10e23ac0bc2b0" dependencies = [ "serde", ] @@ -2383,7 +2312,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -2447,9 +2376,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -2558,11 +2487,11 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "as-raw-xcb-connection", - "ashpd 0.11.0", + "ashpd", "async-task", "bindgen", "bitflags 2.10.0", @@ -2630,6 +2559,7 @@ dependencies = [ "stacksafe", "strum 0.27.2", "sum_tree", + "swash", "taffy", "thiserror 2.0.17", "usvg", @@ -2658,18 +2588,18 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "gpui", @@ -2685,9 +2615,9 @@ checksum = "12101ecc8225ea6d675bc70263074eab6169079621c2186fe0c66590b2df9681" [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", @@ -2891,7 +2821,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "async-compression", @@ -2916,7 +2846,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3183,9 +3113,9 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -3195,9 +3125,9 @@ dependencies = [ [[package]] name = "indexset" -version = "0.12.3" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff794cab64c942437d60272e215f923d466b23dfa6c999cdd0cafe5b6d170805" +checksum = "ceb59e93f24cfa0ebb2d19943356df8d4217ef0b291cc3db969f280498aadbd2" dependencies = [ "ftree", ] @@ -3229,7 +3159,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -3261,9 +3191,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -3308,9 +3238,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jni" @@ -3432,9 +3362,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" -version = "0.2.178" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libfuzzer-sys" @@ -3464,13 +3394,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -3555,9 +3485,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" [[package]] name = "lru-slab" @@ -3677,7 +3607,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "bindgen", @@ -3827,7 +3757,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] @@ -3839,7 +3769,7 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", "security-framework 2.11.1", @@ -3887,7 +3817,6 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", - "memoffset", ] [[package]] @@ -3918,7 +3847,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "aes", "base64", @@ -3943,7 +3872,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "async-utility", "nostr", @@ -3955,7 +3884,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "btreecap", "flatbuffers", @@ -3967,7 +3896,7 @@ dependencies = [ [[package]] name = "nostr-gossip" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "nostr", ] @@ -3975,7 +3904,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "async-utility", "flume", @@ -3989,7 +3918,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "async-utility", "async-wsocket", @@ -4006,7 +3935,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr#038e66a5eb003048b17756bbb525a06edb2eea87" +source = "git+https://github.com/rust-nostr/nostr#78e3b86e736c470ca7241e7383d3886721fc64c2" dependencies = [ "async-utility", "nostr", @@ -4093,7 +4022,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -4302,7 +4231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3299dd401feaf1d45afd8fd1c0586f10fcfb22f244bb9afa942cec73503b89d" dependencies = [ "aes", - "ashpd 0.12.0", + "ashpd", "async-fs", "async-io", "async-lock", @@ -4370,7 +4299,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -4379,6 +4308,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" + [[package]] name = "openssl-sys" version = "0.9.111" @@ -4523,7 +4458,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "collections", "serde", @@ -4536,8 +4471,8 @@ version = "0.3.0" dependencies = [ "anyhow", "common", + "flume", "gpui", - "itertools 0.13.0", "log", "nostr-sdk", "smallvec", @@ -4575,7 +4510,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -4610,7 +4545,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -4741,7 +4676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -4772,14 +4707,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -4800,7 +4735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -4857,9 +4792,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] @@ -4921,9 +4856,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -4952,7 +4887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.4", ] [[package]] @@ -4972,7 +4907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.4", ] [[package]] @@ -4981,14 +4916,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ "getrandom 0.3.4", ] @@ -5117,9 +5052,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ "bitflags 2.10.0", ] @@ -5130,7 +5065,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 1.0.69", ] @@ -5152,13 +5087,13 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "derive_refineable", ] @@ -5198,6 +5133,7 @@ version = "0.3.0" dependencies = [ "anyhow", "common", + "flume", "gpui", "log", "nostr-sdk", @@ -5256,7 +5192,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "bytes", @@ -5301,7 +5237,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -5310,7 +5246,7 @@ dependencies = [ [[package]] name = "rope" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "arrayvec", "log", @@ -5348,7 +5284,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.111", + "syn 2.0.114", "walkdir", ] @@ -5422,9 +5358,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", "log", @@ -5438,11 +5374,11 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.0", "rustls-pki-types", "schannel", "security-framework 3.5.1", @@ -5549,9 +5485,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "salsa20" @@ -5603,7 +5539,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -5716,9 +5652,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" [[package]] name = "semver" @@ -5757,7 +5693,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -5768,7 +5704,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -5782,9 +5718,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.147" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "indexmap", "itoa", @@ -5815,7 +5751,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6074,7 +6010,7 @@ checksum = "172175341049678163e979d9107ca3508046d4d2a7c6682bee46ac541b17db69" dependencies = [ "proc-macro-error2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6083,14 +6019,12 @@ version = "0.3.0" dependencies = [ "anyhow", "common", + "flume", "gpui", "log", "nostr-lmdb", "nostr-sdk", "rustls", - "serde", - "serde_json", - "smallvec", "smol", ] @@ -6137,7 +6071,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6149,7 +6083,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6161,7 +6095,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "arrayvec", "log", @@ -6288,9 +6222,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -6323,7 +6257,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6473,7 +6407,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6484,7 +6418,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6589,9 +6523,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -6610,7 +6544,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6663,9 +6597,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -6688,9 +6622,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap", "serde_core", @@ -6768,9 +6702,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -6831,7 +6765,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -6963,9 +6897,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-bidi" @@ -7066,14 +7000,15 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -7118,7 +7053,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "async-fs", @@ -7154,11 +7089,11 @@ dependencies = [ [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "perf", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -7350,7 +7285,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", "wasm-bindgen-shared", ] @@ -7378,9 +7313,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" dependencies = [ "cc", "downcast-rs", @@ -7392,9 +7327,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.11" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" dependencies = [ "bitflags 2.10.0", "rustix 1.1.3", @@ -7404,9 +7339,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.11" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" +checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" dependencies = [ "rustix 1.1.3", "wayland-client", @@ -7415,9 +7350,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.9" +version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" dependencies = [ "bitflags 2.10.0", "wayland-backend", @@ -7427,9 +7362,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" +checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" dependencies = [ "bitflags 2.10.0", "wayland-backend", @@ -7440,9 +7375,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" dependencies = [ "bitflags 2.10.0", "wayland-backend", @@ -7453,20 +7388,20 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" dependencies = [ "proc-macro2", - "quick-xml 0.37.5", + "quick-xml 0.38.4", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" dependencies = [ "dlib", "log", @@ -7516,14 +7451,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.4", + "webpki-root-certs 1.0.5", ] [[package]] name = "webpki-root-certs" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" dependencies = [ "rustls-pki-types", ] @@ -7534,14 +7469,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.4", + "webpki-roots 1.0.5", ] [[package]] name = "webpki-roots" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -7708,7 +7643,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -7719,7 +7654,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -7730,7 +7665,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -7741,7 +7676,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -8256,9 +8191,9 @@ dependencies = [ [[package]] name = "xcb" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" +checksum = "ee4c580d8205abb0a5cf4eb7e927bd664e425b6c3263f9c5310583da96970cf6" dependencies = [ "bitflags 1.3.2", "libc", @@ -8354,15 +8289,15 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", "synstructure", ] [[package]] name = "zbus" -version = "5.12.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" +checksum = "17f79257df967b6779afa536788657777a0001f5b42524fcaf5038d4344df40b" dependencies = [ "async-broadcast", "async-executor", @@ -8378,8 +8313,9 @@ dependencies = [ "futures-core", "futures-lite 2.6.1", "hex", - "nix 0.30.1", + "libc", "ordered-stream", + "rustix 1.1.3", "serde", "serde_repr", "tracing", @@ -8394,14 +8330,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.12.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" +checksum = "aad23e2d2f91cae771c7af7a630a49e755f1eb74f8a46e9f6d5f7a146edf5a37" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", "zbus_names", "zvariant", "zvariant_utils", @@ -8409,12 +8345,11 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "static_assertions", "winnow", "zvariant", ] @@ -8534,22 +8469,22 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -8569,7 +8504,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", "synstructure", ] @@ -8584,13 +8519,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] @@ -8623,13 +8558,13 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", ] [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "anyhow", "chrono", @@ -8639,14 +8574,14 @@ dependencies = [ [[package]] name = "zmij" -version = "0.1.9" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0095ecd462946aa3927d9297b63ef82fb9a5316d7a37d134eeb36e58228615a" +checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" dependencies = [ "tracing", "tracing-subscriber", @@ -8657,7 +8592,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#f03987fb68224e3b7fb993ce2ffaf364836a003c" +source = "git+https://github.com/zed-industries/zed#024485133d7294263604e25c85d65dc71608a29f" [[package]] name = "zune-core" @@ -8700,9 +8635,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.8.0" +version = "5.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c" +checksum = "326aaed414f04fe839777b4c443d4e94c74e7b3621093bd9c5e649ac8aa96543" dependencies = [ "endi", "enumflags2", @@ -8715,26 +8650,26 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.8.0" +version = "5.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006" +checksum = "ba44e1f8f4da9e6e2d25d2a60b116ef8b9d0be174a7685e55bb12a99866279a7" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.114", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.111", + "syn 2.0.114", "winnow", ] diff --git a/crates/account/Cargo.toml b/crates/account/Cargo.toml deleted file mode 100644 index a594b16..0000000 --- a/crates/account/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "account" -version.workspace = true -edition.workspace = true -publish.workspace = true - -[dependencies] -state = { path = "../state" } -settings = { path = "../settings" } -common = { path = "../common" } -theme = { path = "../theme" } -ui = { path = "../ui" } - -gpui.workspace = true -nostr-sdk.workspace = true - -anyhow.workspace = true -smallvec.workspace = true -smol.workspace = true -log.workspace = true -serde.workspace = true -serde_json.workspace = true diff --git a/crates/account/src/lib.rs b/crates/account/src/lib.rs deleted file mode 100644 index 92a088b..0000000 --- a/crates/account/src/lib.rs +++ /dev/null @@ -1,208 +0,0 @@ -use std::time::Duration; - -use anyhow::Error; -use common::BOOTSTRAP_RELAYS; -use gpui::{App, AppContext, Context, Entity, Global, Task}; -use nostr_sdk::prelude::*; -use smallvec::{smallvec, SmallVec}; -use state::NostrRegistry; - -pub fn init(cx: &mut App) { - Account::set_global(cx.new(Account::new), cx); -} - -struct GlobalAccount(Entity); - -impl Global for GlobalAccount {} - -pub struct Account { - /// The public key of the account - public_key: Option, - - /// Status of the current user NIP-65 relays - pub nip65_status: Entity, - - /// Status of the current user NIP-17 relays - pub nip17_status: Entity, - - /// Tasks for asynchronous operations - _tasks: SmallVec<[Task<()>; 2]>, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum RelayStatus { - #[default] - Initial, - NotSet, - Set, -} - -impl Account { - /// Retrieve the global account state - pub fn global(cx: &App) -> Entity { - cx.global::().0.clone() - } - - /// Check if the global account state exists - pub fn has_global(cx: &App) -> bool { - cx.has_global::() - } - - /// Remove the global account state - pub fn remove_global(cx: &mut App) { - cx.remove_global::(); - } - - /// Set the global account instance - fn set_global(state: Entity, cx: &mut App) { - cx.set_global(GlobalAccount(state)); - } - - /// Create a new account instance - fn new(cx: &mut Context) -> Self { - let nostr = NostrRegistry::global(cx); - let client = nostr.read(cx).client(); - - let nip65_status = cx.new(|_| RelayStatus::default()); - let nip17_status = cx.new(|_| RelayStatus::default()); - - let mut tasks = smallvec![]; - - tasks.push( - // Observe the nostr signer and set the public key when it sets - cx.spawn(async move |this, cx| { - let result = cx - .background_spawn(async move { Self::observe_signer(&client).await }) - .await; - - if let Some(public_key) = result { - this.update(cx, |this, cx| { - this.set_account(public_key, cx); - }) - .expect("Entity has been released") - } - }), - ); - - Self { - public_key: None, - nip65_status, - nip17_status, - _tasks: tasks, - } - } - - /// Observe the signer and return the public key when it sets - async fn observe_signer(client: &Client) -> Option { - let loop_duration = Duration::from_millis(800); - - loop { - if let Ok(signer) = client.signer().await { - if let Ok(public_key) = signer.get_public_key().await { - // Get current user's gossip relays - Self::get_gossip_relays(client, public_key).await.ok()?; - - return Some(public_key); - } - } - smol::Timer::after(loop_duration).await; - } - } - - /// Get gossip relays for a given public key - async fn get_gossip_relays(client: &Client, public_key: PublicKey) -> Result<(), Error> { - let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); - - let filter = Filter::new() - .kind(Kind::RelayList) - .author(public_key) - .limit(1); - - // Subscribe to events from the bootstrapping relays - client - .subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts)) - .await?; - - Ok(()) - } - - /// Ensure the user has NIP-65 relays - async fn ensure_nip65_relays(client: &Client, public_key: PublicKey) -> Result { - let filter = Filter::new() - .kind(Kind::RelayList) - .author(public_key) - .limit(1); - - // Count the number of nip65 relays event in the database - let total = client.database().count(filter).await.unwrap_or(0); - - Ok(total > 0) - } - - /// Ensure the user has NIP-17 relays - async fn ensure_nip17_relays(client: &Client, public_key: PublicKey) -> Result { - let filter = Filter::new() - .kind(Kind::InboxRelays) - .author(public_key) - .limit(1); - - // Count the number of nip17 relays event in the database - let total = client.database().count(filter).await.unwrap_or(0); - - Ok(total > 0) - } - - /// Set the public key of the account - pub fn set_account(&mut self, public_key: PublicKey, cx: &mut Context) { - let nostr = NostrRegistry::global(cx); - let client = nostr.read(cx).client(); - - // Update account's public key - self.public_key = Some(public_key); - - // Add background task - self._tasks.push( - // Verify user's nip65 and nip17 relays - cx.spawn(async move |this, cx| { - cx.background_executor().timer(Duration::from_secs(5)).await; - - // Fetch the NIP-65 relays event in the local database - let ensure_nip65 = Self::ensure_nip65_relays(&client, public_key).await; - - // Fetch the NIP-17 relays event in the local database - let ensure_nip17 = Self::ensure_nip17_relays(&client, public_key).await; - - this.update(cx, |this, cx| { - this.nip65_status.update(cx, |this, cx| { - *this = match ensure_nip65 { - Ok(true) => RelayStatus::Set, - _ => RelayStatus::NotSet, - }; - cx.notify(); - }); - this.nip17_status.update(cx, |this, cx| { - *this = match ensure_nip17 { - Ok(true) => RelayStatus::Set, - _ => RelayStatus::NotSet, - }; - cx.notify(); - }); - }) - .expect("Entity has been released") - }), - ); - - cx.notify(); - } - - /// Check if the account entity has a public key - pub fn has_account(&self) -> bool { - self.public_key.is_some() - } - - /// Get the public key of the account - pub fn public_key(&self) -> PublicKey { - // This method is only called when user is logged in, so unwrap safely - self.public_key.unwrap() - } -} diff --git a/crates/auto_update/src/lib.rs b/crates/auto_update/src/lib.rs index a6bc986..de57b71 100644 --- a/crates/auto_update/src/lib.rs +++ b/crates/auto_update/src/lib.rs @@ -253,12 +253,10 @@ impl AutoUpdater { } fn check_for_updates(version: Version, cx: &AsyncApp) -> Task, Error>> { - let Ok(client) = cx.update(|cx| { + let client = cx.update(|cx| { let nostr = NostrRegistry::global(cx); nostr.read(cx).client() - }) else { - return Task::ready(Err(anyhow!("Entity has been released"))); - }; + }); cx.background_spawn(async move { let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); @@ -416,7 +414,7 @@ async fn install_release_macos( downloaded_dmg: PathBuf, cx: &AsyncApp, ) -> Result<(), Error> { - let running_app_path = cx.update(|cx| cx.app_path())??; + let running_app_path = cx.update(|cx| cx.app_path())?; let running_app_filename = running_app_path .file_name() .with_context(|| format!("invalid running app path {running_app_path:?}"))?; diff --git a/crates/chat/Cargo.toml b/crates/chat/Cargo.toml index 9dbb715..a37256b 100644 --- a/crates/chat/Cargo.toml +++ b/crates/chat/Cargo.toml @@ -7,8 +7,7 @@ publish.workspace = true [dependencies] common = { path = "../common" } state = { path = "../state" } -account = { path = "../account" } -encryption = { path = "../encryption" } +device = { path = "../device" } person = { path = "../person" } settings = { path = "../settings" } diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index 4262913..70a5306 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -5,24 +5,26 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; -use account::Account; use anyhow::{anyhow, Context as AnyhowContext, Error}; -use common::{EventUtils, BOOTSTRAP_RELAYS, METADATA_BATCH_LIMIT}; -use encryption::Encryption; +use common::EventUtils; +use device::DeviceRegistry; use flume::Sender; use fuzzy_matcher::skim::SkimMatcherV2; use fuzzy_matcher::FuzzyMatcher; -use gpui::{App, AppContext, Context, Entity, EventEmitter, Global, Subscription, Task}; -pub use message::*; +use gpui::{ + App, AppContext, Context, Entity, EventEmitter, Global, Subscription, Task, WeakEntity, +}; use nostr_sdk::prelude::*; -pub use room::*; use settings::AppSettings; use smallvec::{smallvec, SmallVec}; -use state::{initialized_at, NostrRegistry, GIFTWRAP_SUBSCRIPTION}; +use state::{tracker, NostrRegistry, GIFTWRAP_SUBSCRIPTION}; mod message; mod room; +pub use message::*; +pub use room::*; + pub fn init(cx: &mut App) { ChatRegistry::set_global(cx.new(ChatRegistry::new), cx); } @@ -31,37 +33,51 @@ struct GlobalChatRegistry(Entity); impl Global for GlobalChatRegistry {} +/// Chat event. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum ChatEvent { + /// An event to open a room by its ID + OpenRoom(u64), + /// An event to close a room by its ID + CloseRoom(u64), + /// An event to notify UI about a new chat request + Ping, +} + +/// Channel signal. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum NostrEvent { + /// Message received from relay pool + Message(NewMessage), + /// Unwrapping status + Unwrapping(bool), + /// Eose received from relay pool + Eose, +} + /// Chat Registry #[derive(Debug)] pub struct ChatRegistry { /// Collection of all chat rooms - pub rooms: Vec>, + rooms: Vec>, /// Loading status of the registry - pub loading: bool, + loading: bool, - /// Async task for handling notifications - handle_notifications: Task<()>, + /// Tracking the status of unwrapping gift wrap events. + tracking_flag: Arc, - /// Event subscriptions - _subscriptions: SmallVec<[Subscription; 1]>, + /// Channel's sender for communication between nostr and gpui + sender: Sender, + + /// Handle notifications asynchronous task + notifications: Option>>, /// Tasks for asynchronous operations - _tasks: SmallVec<[Task<()>; 4]>, -} + tasks: Vec>, -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum ChatEvent { - OpenRoom(u64), - CloseRoom(u64), - NewChatRequest(RoomKind), -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Signal { - Loading(bool), - Message(NewMessage), - Eose, + /// Subscriptions + _subscriptions: SmallVec<[Subscription; 1]>, } impl EventEmitter for ChatRegistry {} @@ -79,81 +95,65 @@ impl ChatRegistry { /// Create a new chat registry instance fn new(cx: &mut Context) -> Self { - let encryption = Encryption::global(cx); - let encryption_key = encryption.read(cx).encryption.clone(); - let nostr = NostrRegistry::global(cx); - let client = nostr.read(cx).client(); + let identity = nostr.read(cx).identity(); - let status = Arc::new(AtomicBool::new(true)); - let (tx, rx) = flume::bounded::(2048); + let device = DeviceRegistry::global(cx); + let device_signer = device.read(cx).device_signer.clone(); - let handle_notifications = cx.background_spawn({ - let client = nostr.read(cx).client(); - let status = Arc::clone(&status); - let tx = tx.clone(); - let signer: Option> = None; + // A flag to indicate if the registry is loading + let tracking_flag = Arc::new(AtomicBool::new(true)); - async move { Self::handle_notifications(&client, &signer, &tx, &status).await } - }); + // Channel for communication between nostr and gpui + let (tx, rx) = flume::bounded::(2048); + let mut tasks = vec![]; let mut subscriptions = smallvec![]; - let mut tasks = smallvec![]; subscriptions.push( - // Observe the encryption global state - cx.observe(&encryption_key, { - let status = Arc::clone(&status); - let tx = tx.clone(); + // Observe the identity + cx.observe(&identity, |this, state, cx| { + if state.read(cx).has_public_key() { + // Handle nostr notifications + this.handle_notifications(cx); + // Track unwrapping progress + this.tracking(cx); + } + }), + ); - move |this, state, cx| { - if let Some(signer) = state.read(cx).clone() { - this.handle_notifications = cx.background_spawn({ - let client = nostr.read(cx).client(); - let status = Arc::clone(&status); - let tx = tx.clone(); - let signer = Some(signer); - - async move { - Self::handle_notifications(&client, &signer, &tx, &status).await - } - }); - cx.notify(); - } + subscriptions.push( + // Observe the device signer state + cx.observe(&device_signer, |this, state, cx| { + if state.read(cx).is_some() { + this.handle_notifications(cx); } }), ); tasks.push( - // Handle unwrapping status - cx.background_spawn( - async move { Self::handle_unwrapping(&client, &status, &tx).await }, - ), - ); - - tasks.push( - // Handle new messages + // Update GPUI states cx.spawn(async move |this, cx| { while let Ok(message) = rx.recv_async().await { match message { - Signal::Message(message) => { + NostrEvent::Message(message) => { this.update(cx, |this, cx| { this.new_message(message, cx); }) - .expect("Entity has been released"); + .ok(); } - Signal::Eose => { + NostrEvent::Eose => { this.update(cx, |this, cx| { this.get_rooms(cx); }) - .expect("Entity has been released"); + .ok(); } - Signal::Loading(status) => { + NostrEvent::Unwrapping(status) => { this.update(cx, |this, cx| { this.set_loading(status, cx); this.get_rooms(cx); }) - .expect("Entity has been released"); + .ok(); } }; } @@ -163,127 +163,137 @@ impl ChatRegistry { Self { rooms: vec![], loading: true, - handle_notifications, + tracking_flag, + sender: tx.clone(), + notifications: None, + tasks, _subscriptions: subscriptions, - _tasks: tasks, } } - async fn handle_notifications( - client: &Client, - signer: &Option, - tx: &Sender, - status: &Arc, - ) where - T: NostrSigner, - { - let initialized_at = initialized_at(); - let subscription_id = SubscriptionId::new(GIFTWRAP_SUBSCRIPTION); + /// Handle nostr notifications + fn handle_notifications(&mut self, cx: &mut Context) { + let nostr = NostrRegistry::global(cx); + let client = nostr.read(cx).client(); - let mut notifications = client.notifications(); - let mut public_keys = HashSet::new(); - let mut processed_events = HashSet::new(); + let device = DeviceRegistry::global(cx); + let device_signer = device.read(cx).signer(cx); - while let Ok(notification) = notifications.recv().await { - let RelayPoolNotification::Message { message, .. } = notification else { - // Skip non-message notifications - continue; - }; + let status = self.tracking_flag.clone(); + let tx = self.sender.clone(); - match message { - RelayMessage::Event { event, .. } => { - if !processed_events.insert(event.id) { - // Skip if the event has already been processed - continue; - } + self.tasks.push(cx.background_spawn(async move { + let initialized_at = Timestamp::now(); + let subscription_id = SubscriptionId::new(GIFTWRAP_SUBSCRIPTION); - if event.kind != Kind::GiftWrap { - // Skip non-gift wrap events - continue; - } + let mut notifications = client.notifications(); + let mut processed_events = HashSet::new(); - // Extract the rumor from the gift wrap event - match Self::extract_rumor(client, signer, event.as_ref()).await { - Ok(rumor) => { - // Get all public keys - public_keys.extend(rumor.all_pubkeys()); + while let Ok(notification) = notifications.recv().await { + let RelayPoolNotification::Message { message, .. } = notification else { + // Skip non-message notifications + continue; + }; - let limit_reached = public_keys.len() >= METADATA_BATCH_LIMIT; - let done = !status.load(Ordering::Acquire) && !public_keys.is_empty(); + match message { + RelayMessage::Event { event, .. } => { + if !processed_events.insert(event.id) { + // Skip if the event has already been processed + continue; + } - // Get metadata for all public keys if the limit is reached - if limit_reached || done { - let public_keys = std::mem::take(&mut public_keys); - // Get metadata for the public keys - Self::get_metadata(client, public_keys).await.ok(); - } + if event.kind != Kind::GiftWrap { + // Skip non-gift wrap events + continue; + } - match &rumor.created_at >= initialized_at { + // Extract the rumor from the gift wrap event + match Self::extract_rumor(&client, &device_signer, event.as_ref()).await { + Ok(rumor) => match rumor.created_at >= initialized_at { true => { - let new_message = NewMessage::new(event.id, rumor); - let signal = Signal::Message(new_message); + // Check if the event is sent by coop + let sent_by_coop = { + let tracker = tracker().read().await; + tracker.is_sent_by_coop(&event.id) + }; + // No need to emit if sent by coop + // the event is already emitted + if !sent_by_coop { + let new_message = NewMessage::new(event.id, rumor); + let signal = NostrEvent::Message(new_message); - if let Err(e) = tx.send_async(signal).await { - log::error!("Failed to send signal: {}", e); + tx.send_async(signal).await.ok(); } } false => { status.store(true, Ordering::Release); } + }, + Err(e) => { + log::warn!("Failed to unwrap: {e}"); } } - Err(e) => { - log::warn!("Failed to unwrap gift wrap event: {}", e); + } + RelayMessage::EndOfStoredEvents(id) => { + if id.as_ref() == &subscription_id { + tx.send_async(NostrEvent::Eose).await.ok(); } } + _ => {} } - RelayMessage::EndOfStoredEvents(id) => { - if id.as_ref() == &subscription_id { - if let Err(e) = tx.send_async(Signal::Eose).await { - log::error!("Failed to send signal: {}", e); - } - } - } - _ => {} } - } + })); } - async fn handle_unwrapping(client: &Client, status: &Arc, tx: &Sender) { - let loop_duration = Duration::from_secs(20); - let mut is_start_processing = false; - let mut total_loops = 0; + /// Tracking the status of unwrapping gift wrap events. + fn tracking(&mut self, cx: &mut Context) { + let nostr = NostrRegistry::global(cx); + let client = nostr.read(cx).client(); - loop { - if client.has_signer().await { - total_loops += 1; + let status = self.tracking_flag.clone(); + let tx = self.sender.clone(); - if status.load(Ordering::Acquire) { - is_start_processing = true; + self.notifications = Some(cx.background_spawn(async move { + let loop_duration = Duration::from_secs(12); - // Reset gift wrap processing flag - _ = status.compare_exchange(true, false, Ordering::Release, Ordering::Relaxed); + let mut is_start_processing = false; + let mut total_loops = 0; - // Send loading signal - if let Err(e) = tx.send_async(Signal::Loading(true)).await { - log::error!("Failed to send signal: {}", e); - } - } else { - // Only run further if we are already processing - // Wait until after 2 loops to prevent exiting early while events are still being processed - if is_start_processing && total_loops >= 2 { - // Send loading signal - if let Err(e) = tx.send_async(Signal::Loading(false)).await { - log::error!("Failed to send signal: {}", e); + loop { + if client.has_signer().await { + total_loops += 1; + + if status.load(Ordering::Acquire) { + is_start_processing = true; + // Reset gift wrap processing flag + _ = status.compare_exchange( + true, + false, + Ordering::Release, + Ordering::Relaxed, + ); + + tx.send_async(NostrEvent::Unwrapping(true)).await.ok(); + } else { + // Only run further if we are already processing + // Wait until after 2 loops to prevent exiting early while events are still being processed + if is_start_processing && total_loops >= 2 { + tx.send_async(NostrEvent::Unwrapping(false)).await.ok(); + + // Reset the counter + is_start_processing = false; + total_loops = 0; } - // Reset the counter - is_start_processing = false; - total_loops = 0; } } + smol::Timer::after(loop_duration).await; } - smol::Timer::after(loop_duration).await; - } + })); + } + + /// Get the loading status of the chat registry + pub fn loading(&self) -> bool { + self.loading } /// Set the loading status of the chat registry @@ -292,12 +302,12 @@ impl ChatRegistry { cx.notify(); } - /// Get a room by its ID. - pub fn room(&self, id: &u64, cx: &App) -> Option> { + /// Get a weak reference to a room by its ID. + pub fn room(&self, id: &u64, cx: &App) -> Option> { self.rooms .iter() - .find(|model| model.read(cx).id == *id) - .cloned() + .find(|this| &this.read(cx).id == id) + .map(|this| this.downgrade()) } /// Get all ongoing rooms. @@ -319,11 +329,30 @@ impl ChatRegistry { } /// Add a new room to the start of list. - pub fn add_room(&mut self, room: Entity, cx: &mut Context) { - self.rooms.insert(0, room); + pub fn add_room(&mut self, room: I, cx: &mut Context) + where + I: Into, + { + self.rooms.insert(0, cx.new(|_| room.into())); cx.notify(); } + /// Emit an open room event. + /// If the room is new, add it to the registry. + pub fn emit_room(&mut self, room: WeakEntity, cx: &mut Context) { + if let Some(room) = room.upgrade() { + let id = room.read(cx).id; + + // If the room is new, add it to the registry. + if !self.rooms.iter().any(|r| r.read(cx).id == id) { + self.rooms.insert(0, room); + } + + // Emit the open room event. + cx.emit(ChatEvent::OpenRoom(id)); + } + } + /// Close a room. pub fn close_room(&mut self, id: u64, cx: &mut Context) { if self.rooms.iter().any(|r| r.read(cx).id == id) { @@ -367,17 +396,6 @@ impl ChatRegistry { cx.notify(); } - /// Push a new room to the chat registry - pub fn push_room(&mut self, room: Entity, cx: &mut Context) { - let id = room.read(cx).id; - - if !self.rooms.iter().any(|r| r.read(cx).id == id) { - self.add_room(room, cx); - } - - cx.emit(ChatEvent::OpenRoom(id)); - } - /// Extend the registry with new rooms. fn extend_rooms(&mut self, rooms: HashSet, cx: &mut Context) { let mut room_map: HashMap = self @@ -410,7 +428,7 @@ impl ChatRegistry { pub fn get_rooms(&mut self, cx: &mut Context) { let task = self.create_get_rooms_task(cx); - self._tasks.push( + self.tasks.push( // Run and finished in the background cx.spawn(async move |this, cx| { match task.await { @@ -528,59 +546,61 @@ impl ChatRegistry { /// 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) { + let nostr = NostrRegistry::global(cx); + // Get the unique id let id = message.rumor.uniq_id(); + // Get the author let author = message.rumor.pubkey; - let account = Account::global(cx); - if let Some(room) = self.rooms.iter().find(|room| room.read(cx).id == id) { - let is_new_event = message.rumor.created_at > room.read(cx).created_at; - let created_at = message.rumor.created_at; - let event_for_emit = message.rumor.clone(); + 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| { - if is_new_event { - this.set_created_at(created_at, cx); + // 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); } - - // Set this room is ongoing if the new message is from current user - if author == account.read(cx).public_key() { - this.set_ongoing(cx); - } - - // Emit the new message to the room - this.emit_message(message.gift_wrap, event_for_emit.clone(), cx); - }); - - // Resort all rooms in the registry by their created at (after updated) - if is_new_event { - self.sort(cx); } - } else { - // Push the new room to the front of the list - self.add_room(cx.new(|_| Room::from(&message.rumor)), 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::NewChatRequest(RoomKind::default())); + // Notify the UI about the new room + cx.emit(ChatEvent::Ping); + } } } // Unwraps a gift-wrapped event and processes its contents. - async fn extract_rumor( + async fn extract_rumor( client: &Client, - signer: &Option, + device_signer: &Option>, gift_wrap: &Event, - ) -> Result - where - T: NostrSigner, - { + ) -> Result { // Try to get cached rumor first if let Ok(event) = Self::get_rumor(client, gift_wrap.id).await { return Ok(event); } // Try to unwrap with the available signer - let unwrapped = Self::try_unwrap(client, signer, gift_wrap).await?; + let unwrapped = Self::try_unwrap(client, device_signer, gift_wrap).await?; let mut rumor_unsigned = unwrapped.rumor; // Generate event id for the rumor if it doesn't have one @@ -593,37 +613,26 @@ impl ChatRegistry { } // Helper method to try unwrapping with different signers - async fn try_unwrap( + async fn try_unwrap( client: &Client, - signer: &Option, + device_signer: &Option>, gift_wrap: &Event, - ) -> Result - where - T: NostrSigner, - { - if let Some(custom_signer) = signer.as_ref() { - if let Ok(seal) = custom_signer + ) -> Result { + if let Some(signer) = device_signer.as_ref() { + let seal = signer .nip44_decrypt(&gift_wrap.pubkey, &gift_wrap.content) - .await - { - let seal: Event = Event::from_json(seal)?; - seal.verify_with_ctx(&SECP256K1)?; + .await?; - // Decrypt the rumor - // TODO: verify the sender - let rumor = custom_signer - .nip44_decrypt(&seal.pubkey, &seal.content) - .await?; + let seal: Event = Event::from_json(seal)?; + seal.verify_with_ctx(&SECP256K1)?; - // Construct the unsigned event - let rumor = UnsignedEvent::from_json(rumor)?; + let rumor = signer.nip44_decrypt(&seal.pubkey, &seal.content).await?; + let rumor = UnsignedEvent::from_json(rumor)?; - // Return the unwrapped gift - return Ok(UnwrappedGift { - sender: rumor.pubkey, - rumor, - }); - } + return Ok(UnwrappedGift { + sender: seal.pubkey, + rumor, + }); } let signer = client.signer().await?; @@ -695,33 +704,6 @@ impl ChatRegistry { } } - /// Get metadata for a list of public keys - async fn get_metadata(client: &Client, public_keys: I) -> Result<(), Error> - where - I: IntoIterator, - { - let authors: Vec = public_keys.into_iter().collect(); - let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); - let kinds = vec![Kind::Metadata, Kind::ContactList]; - - // Return if the list is empty - if authors.is_empty() { - return Err(anyhow!("You need at least one public key".to_string(),)); - } - - let filter = Filter::new() - .limit(authors.len() * kinds.len()) - .authors(authors) - .kinds(kinds); - - // Subscribe to filters to the bootstrap relays - client - .subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts)) - .await?; - - Ok(()) - } - /// Get the conversation ID for a given rumor (message). fn conversation_id(rumor: &UnsignedEvent) -> u64 { let mut hasher = DefaultHasher::new(); diff --git a/crates/chat/src/message.rs b/crates/chat/src/message.rs index b0e34ef..c4cfef6 100644 --- a/crates/chat/src/message.rs +++ b/crates/chat/src/message.rs @@ -2,6 +2,7 @@ use std::hash::Hash; use nostr_sdk::prelude::*; +/// New message. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct NewMessage { pub gift_wrap: EventId, @@ -14,6 +15,7 @@ impl NewMessage { } } +/// Message. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Message { User(RenderedMessage), @@ -22,11 +24,17 @@ pub enum Message { } impl Message { - pub fn user(user: impl Into) -> Self { + pub fn user(user: I) -> Self + where + I: Into, + { Self::User(user.into()) } - pub fn warning(content: impl Into) -> Self { + pub fn warning(content: I) -> Self + where + I: Into, + { Self::Warning(content.into(), Timestamp::now()) } @@ -43,6 +51,18 @@ impl Message { } } +impl From<&NewMessage> for Message { + fn from(val: &NewMessage) -> Self { + Self::User(val.into()) + } +} + +impl From<&UnsignedEvent> for Message { + fn from(val: &UnsignedEvent) -> Self { + Self::User(val.into()) + } +} + impl Ord for Message { fn cmp(&self, other: &Self) -> std::cmp::Ordering { match (self, other) { @@ -63,6 +83,7 @@ impl PartialOrd for Message { } } +/// Rendered message. #[derive(Debug, Clone)] pub struct RenderedMessage { pub id: EventId, @@ -78,48 +99,53 @@ pub struct RenderedMessage { pub replies_to: Vec, } -impl From for RenderedMessage { - fn from(inner: Event) -> Self { - let mentions = extract_mentions(&inner.content); - let replies_to = extract_reply_ids(&inner.tags); +impl From<&Event> for RenderedMessage { + fn from(val: &Event) -> Self { + let mentions = extract_mentions(&val.content); + let replies_to = extract_reply_ids(&val.tags); Self { - id: inner.id, - author: inner.pubkey, - content: inner.content, - created_at: inner.created_at, + id: val.id, + author: val.pubkey, + content: val.content.clone(), + created_at: val.created_at, mentions, replies_to, } } } -impl From for RenderedMessage { - fn from(inner: UnsignedEvent) -> Self { - let mentions = extract_mentions(&inner.content); - let replies_to = extract_reply_ids(&inner.tags); +impl From<&UnsignedEvent> for RenderedMessage { + fn from(val: &UnsignedEvent) -> Self { + let mentions = extract_mentions(&val.content); + let replies_to = extract_reply_ids(&val.tags); Self { // Event ID must be known - id: inner.id.unwrap(), - author: inner.pubkey, - content: inner.content, - created_at: inner.created_at, + id: val.id.unwrap(), + author: val.pubkey, + content: val.content.clone(), + created_at: val.created_at, mentions, replies_to, } } } -impl From> for RenderedMessage { - fn from(inner: Box) -> Self { - (*inner).into() - } -} +impl From<&NewMessage> for RenderedMessage { + fn from(val: &NewMessage) -> Self { + let mentions = extract_mentions(&val.rumor.content); + let replies_to = extract_reply_ids(&val.rumor.tags); -impl From<&Box> for RenderedMessage { - fn from(inner: &Box) -> Self { - inner.to_owned().into() + Self { + // Event ID must be known + id: val.rumor.id.unwrap(), + author: val.rumor.pubkey, + content: val.rumor.content.clone(), + created_at: val.rumor.created_at, + mentions, + replies_to, + } } } @@ -149,6 +175,7 @@ impl Hash for RenderedMessage { } } +/// Extracts all mentions (public keys) from a content string. fn extract_mentions(content: &str) -> Vec { let parser = NostrParser::new(); let tokens = parser.parse(content); @@ -165,6 +192,7 @@ fn extract_mentions(content: &str) -> Vec { .collect::>() } +/// Extracts all reply (ids) from the event tags. fn extract_reply_ids(inner: &Tags) -> Vec { let mut replies_to = vec![]; diff --git a/crates/chat/src/room.rs b/crates/chat/src/room.rs index 0ad7878..192c0d9 100644 --- a/crates/chat/src/room.rs +++ b/crates/chat/src/room.rs @@ -3,43 +3,18 @@ use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::time::Duration; -use account::Account; -use anyhow::{anyhow, Error}; -use common::{EventUtils, RenderedProfile}; -use encryption::{Encryption, SignerKind}; +use anyhow::Error; +use common::EventUtils; use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task}; use itertools::Itertools; use nostr_sdk::prelude::*; -use person::PersonRegistry; -use state::NostrRegistry; +use person::{Person, PersonRegistry}; +use state::{tracker, NostrRegistry}; + +use crate::NewMessage; const SEND_RETRY: usize = 10; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct SendOptions { - pub backup: bool, - pub signer_kind: SignerKind, -} - -impl SendOptions { - pub fn new() -> Self { - Self { - backup: true, - signer_kind: SignerKind::default(), - } - } - - pub fn backup(&self) -> bool { - self.backup - } -} - -impl Default for SendOptions { - fn default() -> Self { - Self::new() - } -} - #[derive(Debug, Clone)] pub struct SendReport { pub receiver: PublicKey, @@ -107,17 +82,21 @@ impl SendReport { } } -#[derive(Debug, Clone)] -pub enum RoomSignal { - NewMessage((EventId, UnsignedEvent)), - Refresh, +/// Room event. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum RoomEvent { + /// Incoming message. + Incoming(NewMessage), + /// Reloads the current room's messages. + Reload, } +/// Room kind. #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum RoomKind { - Ongoing, #[default] Request, + Ongoing, } #[derive(Debug)] @@ -160,7 +139,7 @@ impl Hash for Room { impl Eq for Room {} -impl EventEmitter for Room {} +impl EventEmitter for Room {} impl From<&UnsignedEvent> for Room { fn from(val: &UnsignedEvent) -> Self { @@ -168,7 +147,7 @@ impl From<&UnsignedEvent> for Room { let created_at = val.created_at; // Get the members from the event's tags and event's pubkey - let members = val.all_pubkeys(); + let members = val.extract_public_keys(); // Get subject from tags let subject = val @@ -248,6 +227,28 @@ impl Room { self.members.clone() } + /// Returns the members of the room with their messaging relays + pub fn members_with_relays(&self, cx: &App) -> Task)>> { + let nostr = NostrRegistry::global(cx); + let mut tasks = vec![]; + + for member in self.members.iter() { + let task = nostr.read(cx).messaging_relays(member, cx); + tasks.push((*member, task)); + } + + cx.background_spawn(async move { + let mut results = vec![]; + + for (public_key, task) in tasks.into_iter() { + let urls = task.await; + results.push((public_key, urls)); + } + + results + }) + } + /// Checks if the room has more than two members (group) pub fn is_group(&self) -> bool { self.members.len() > 2 @@ -263,9 +264,9 @@ impl Room { } /// Gets the display image for the room - pub fn display_image(&self, proxy: bool, cx: &App) -> SharedString { + pub fn display_image(&self, cx: &App) -> SharedString { if !self.is_group() { - self.display_member(cx).avatar(proxy) + self.display_member(cx).avatar() } else { SharedString::from("brand/group.png") } @@ -274,10 +275,10 @@ impl Room { /// Get a member to represent the room /// /// Display member is always different from the current user. - pub fn display_member(&self, cx: &App) -> Profile { + pub fn display_member(&self, cx: &App) -> Person { let persons = PersonRegistry::global(cx); - let account = Account::global(cx); - let public_key = account.read(cx).public_key(); + let nostr = NostrRegistry::global(cx); + let public_key = nostr.read(cx).identity().read(cx).public_key(); let target_member = self .members @@ -286,7 +287,7 @@ impl Room { .or_else(|| self.members.first()) .expect("Room should have at least one member"); - persons.read(cx).get_person(target_member, cx) + persons.read(cx).get(target_member, cx) } /// Merge the names of the first two members of the room. @@ -294,10 +295,10 @@ impl Room { let persons = PersonRegistry::global(cx); if self.is_group() { - let profiles: Vec = self + let profiles: Vec = self .members .iter() - .map(|public_key| persons.read(cx).get_person(public_key, cx)) + .map(|public_key| persons.read(cx).get(public_key, cx)) .collect(); let mut name = profiles @@ -313,18 +314,18 @@ impl Room { SharedString::from(name) } else { - self.display_member(cx).display_name() + self.display_member(cx).name() } } /// Emits a new message signal to the current room - pub fn emit_message(&self, id: EventId, event: UnsignedEvent, cx: &mut Context) { - cx.emit(RoomSignal::NewMessage((id, event))); + pub fn emit_message(&self, message: NewMessage, cx: &mut Context) { + cx.emit(RoomEvent::Incoming(message)); } - /// Emits a signal to refresh the current room's messages. + /// Emits a signal to reload the current room's messages. pub fn emit_refresh(&mut self, cx: &mut Context) { - cx.emit(RoomSignal::Refresh); + cx.emit(RoomEvent::Reload); } /// Get gossip relays for each member @@ -332,11 +333,16 @@ impl Room { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); let members = self.members(); + let id = SubscriptionId::new(format!("room-{}", self.id)); cx.background_spawn(async move { let signer = client.signer().await?; let public_key = signer.get_public_key().await?; - let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); + + // Subscription options + let opts = SubscribeAutoCloseOptions::default() + .timeout(Some(Duration::from_secs(2))) + .exit_policy(ReqExitPolicy::ExitOnEOSE); for member in members.into_iter() { if member == public_key { @@ -347,7 +353,9 @@ impl Room { let filter = Filter::new().kind(Kind::RelayList).author(member).limit(1); // Subscribe to get member's gossip relays - client.subscribe(filter, Some(opts)).await?; + client + .subscribe_with_id(id.clone(), filter, Some(opts)) + .await?; } Ok(()) @@ -381,12 +389,9 @@ impl Room { /// Create a new message event (unsigned) pub fn create_message(&self, content: &str, replies: &[EventId], cx: &App) -> UnsignedEvent { let nostr = NostrRegistry::global(cx); - let gossip = nostr.read(cx).gossip(); - let read_gossip = gossip.read_blocking(); // Get current user - let account = Account::global(cx); - let public_key = account.read(cx).public_key(); + let public_key = nostr.read(cx).identity().read(cx).public_key(); // Get room's subject let subject = self.subject.clone(); @@ -398,7 +403,7 @@ impl Room { // NOTE: current user will be removed from the list of receivers for member in self.members.iter() { // Get relay hint if available - let relay_url = read_gossip.messaging_relays(member).first().cloned(); + let relay_url = nostr.read(cx).relay_hint(member, cx); // Construct a public key tag with relay hint let tag = TagStandard::PublicKey { @@ -449,98 +454,65 @@ impl Room { pub fn send_message( &self, rumor: &UnsignedEvent, - opts: &SendOptions, cx: &App, ) -> Task, Error>> { - let encryption = Encryption::global(cx); - let encryption_key = encryption.read(cx).encryption_key(cx); - let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - let gossip = nostr.read(cx).gossip(); - let tracker = nostr.read(cx).tracker(); + + // Get current user's public key and relays + let current_user = nostr.read(cx).identity().read(cx).public_key(); + let current_user_relays = nostr.read(cx).messaging_relays(¤t_user, cx); let rumor = rumor.to_owned(); - let opts = opts.to_owned(); - // Get all members - let mut members = self.members(); + // Get all members and their messaging relays + let task = self.members_with_relays(cx); cx.background_spawn(async move { - let signer_kind = opts.signer_kind; - let gossip = gossip.read().await; - - // Get current user's signer and public key - let user_signer = client.signer().await?; - let user_pubkey = user_signer.get_public_key().await?; - - // Get the encryption public key - let encryption_pubkey = if let Some(signer) = encryption_key.as_ref() { - signer.get_public_key().await.ok() - } else { - None - }; + let signer = client.signer().await?; + let current_user_relays = current_user_relays.await; + let mut members = task.await; // Remove the current user's public key from the list of receivers // the current user will be handled separately - members.retain(|&pk| pk != user_pubkey); - - // Determine the signer will be used based on the provided options - let signer = Self::select_signer(&opts.signer_kind, user_signer, encryption_key)?; + members.retain(|(this, _)| this != ¤t_user); // Collect the send reports let mut reports: Vec = vec![]; - for member in members.into_iter() { - // Get user's messaging relays - let urls = gossip.messaging_relays(&member); - // Get user's encryption public key if available - let encryption = gossip.announcement(&member).map(|a| a.public_key()); - + for (receiver, relays) in members.into_iter() { // Check if there are any relays to send the message to - if urls.is_empty() { - reports.push(SendReport::new(member).relays_not_found()); + if relays.is_empty() { + reports.push(SendReport::new(receiver).relays_not_found()); continue; } - // Skip sending if using encryption signer but receiver's encryption keys not found - if encryption.is_none() && matches!(signer_kind, SignerKind::Encryption) { - reports.push(SendReport::new(member).device_not_found()); - continue; + // Ensure relay connection + for url in relays.iter() { + client.add_relay(url).await?; + client.connect_relay(url).await?; } - // Ensure connections to the relays - gossip.ensure_connections(&client, &urls).await; - - // Determine the receiver based on the signer kind - let receiver = Self::select_receiver(&signer_kind, member, encryption)?; - // Construct the gift wrap event - let event = EventBuilder::gift_wrap( - &signer, - &receiver, - rumor.clone(), - vec![Tag::public_key(member)], - ) - .await?; + let event = + EventBuilder::gift_wrap(&signer, &receiver, rumor.clone(), vec![]).await?; // Send the gift wrap event to the messaging relays - match client.send_event_to(urls, &event).await { + match client.send_event_to(relays, &event).await { Ok(output) => { let id = output.id().to_owned(); let auth = output.failed.iter().any(|(_, s)| s.starts_with("auth-")); let report = SendReport::new(receiver).status(output); + let tracker = tracker().read().await; if auth { // Wait for authenticated and resent event successfully for attempt in 0..=SEND_RETRY { - let tracker = tracker.read().await; - let ids = tracker.resent_ids(); - // Check if event was successfully resent - if let Some(output) = ids.iter().find(|e| e.id() == &id).cloned() { - let output = SendReport::new(receiver).status(output); - reports.push(output); + if tracker.is_sent_by_coop(&id) { + let output = Output::new(id); + let report = SendReport::new(receiver).status(output); + reports.push(report); break; } @@ -562,55 +534,35 @@ impl Room { } } - // Return early if the user disabled backup. - // - // Coop will not send a gift wrap event to the current user. - if !opts.backup() { - return Ok(reports); - } - - // Skip sending if using encryption signer but receiver's encryption keys not found - if encryption_pubkey.is_none() && matches!(signer_kind, SignerKind::Encryption) { - reports.push(SendReport::new(user_pubkey).device_not_found()); - return Ok(reports); - } - - // Determine the receiver based on the signer kind - let receiver = Self::select_receiver(&signer_kind, user_pubkey, encryption_pubkey)?; - // Construct the gift-wrapped event - let event = EventBuilder::gift_wrap( - &signer, - &receiver, - rumor.clone(), - vec![Tag::public_key(user_pubkey)], - ) - .await?; + let event = + EventBuilder::gift_wrap(&signer, ¤t_user, rumor.clone(), vec![]).await?; // Only send a backup message to current user if sent successfully to others if reports.iter().all(|r| r.is_sent_success()) { - let urls = gossip.messaging_relays(&user_pubkey); - // Check if there are any relays to send the event to - if urls.is_empty() { - reports.push(SendReport::new(user_pubkey).relays_not_found()); + if current_user_relays.is_empty() { + reports.push(SendReport::new(current_user).relays_not_found()); return Ok(reports); } - // Ensure connections to the relays - gossip.ensure_connections(&client, &urls).await; + // Ensure relay connection + for url in current_user_relays.iter() { + client.add_relay(url).await?; + client.connect_relay(url).await?; + } // Send the event to the messaging relays - match client.send_event_to(urls, &event).await { + match client.send_event_to(current_user_relays, &event).await { Ok(output) => { - reports.push(SendReport::new(user_pubkey).status(output)); + reports.push(SendReport::new(current_user).status(output)); } Err(e) => { - reports.push(SendReport::new(user_pubkey).error(e.to_string())); + reports.push(SendReport::new(current_user).error(e.to_string())); } } } else { - reports.push(SendReport::new(user_pubkey).on_hold(event)); + reports.push(SendReport::new(current_user).on_hold(event)); } Ok(reports) @@ -625,10 +577,8 @@ impl Room { ) -> Task, Error>> { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); - let gossip = nostr.read(cx).gossip(); cx.background_spawn(async move { - let gossip = gossip.read().await; let mut resend_reports = vec![]; for report in reports.into_iter() { @@ -657,23 +607,13 @@ impl Room { // Process the on hold event if it exists if let Some(event) = report.on_hold { - let urls = gossip.messaging_relays(&receiver); - - // Check if there are any relays to send the event to - if urls.is_empty() { - resend_reports.push(SendReport::new(receiver).relays_not_found()); - } else { - // Ensure connections to the relays - gossip.ensure_connections(&client, &urls).await; - - // Send the event to the messaging relays - match client.send_event_to(urls, &event).await { - Ok(output) => { - resend_reports.push(SendReport::new(receiver).status(output)); - } - Err(e) => { - resend_reports.push(SendReport::new(receiver).error(e.to_string())); - } + // Send the event to the messaging relays + match client.send_event(&event).await { + Ok(output) => { + resend_reports.push(SendReport::new(receiver).status(output)); + } + Err(e) => { + resend_reports.push(SendReport::new(receiver).error(e.to_string())); } } } @@ -682,31 +622,4 @@ impl Room { Ok(resend_reports) }) } - - fn select_signer(kind: &SignerKind, user: T, encryption: Option) -> Result - where - T: NostrSigner, - { - match kind { - SignerKind::Encryption => { - Ok(encryption.ok_or_else(|| anyhow!("No encryption key found"))?) - } - SignerKind::User => Ok(user), - SignerKind::Auto => Ok(encryption.unwrap_or(user)), - } - } - - fn select_receiver( - kind: &SignerKind, - member: PublicKey, - encryption: Option, - ) -> Result { - match kind { - SignerKind::Encryption => { - Ok(encryption.ok_or_else(|| anyhow!("Receiver's encryption key not found"))?) - } - SignerKind::User => Ok(member), - SignerKind::Auto => Ok(encryption.unwrap_or(member)), - } - } } diff --git a/crates/chat_ui/Cargo.toml b/crates/chat_ui/Cargo.toml index 9119b98..bff3e8d 100644 --- a/crates/chat_ui/Cargo.toml +++ b/crates/chat_ui/Cargo.toml @@ -9,8 +9,6 @@ state = { path = "../state" } ui = { path = "../ui" } theme = { path = "../theme" } common = { path = "../common" } -account = { path = "../account" } -encryption = { path = "../encryption" } person = { path = "../person" } chat = { path = "../chat" } settings = { path = "../settings" } diff --git a/crates/chat_ui/src/actions.rs b/crates/chat_ui/src/actions.rs index 878bc9c..bea282e 100644 --- a/crates/chat_ui/src/actions.rs +++ b/crates/chat_ui/src/actions.rs @@ -1,4 +1,3 @@ -use encryption::SignerKind; use gpui::Action; use nostr_sdk::prelude::*; use serde::Deserialize; @@ -7,10 +6,6 @@ use serde::Deserialize; #[action(namespace = chat, no_json)] pub struct SeenOn(pub EventId); -#[derive(Action, Clone, PartialEq, Eq, Deserialize)] -#[action(namespace = chat, no_json)] -pub struct SetSigner(pub SignerKind); - /// Define a open public key action #[derive(Action, Clone, PartialEq, Eq, Deserialize, Debug)] #[action(namespace = pubkey, no_json)] diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index bd4ccd5..f1e6900 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -2,22 +2,21 @@ use std::collections::HashSet; use std::time::Duration; pub use actions::*; -use chat::{Message, RenderedMessage, Room, RoomKind, RoomSignal, SendOptions, SendReport}; -use common::{nip96_upload, RenderedProfile, RenderedTimestamp}; -use encryption::SignerKind; +use chat::{Message, RenderedMessage, Room, RoomEvent, RoomKind, SendReport}; +use common::{nip96_upload, RenderedTimestamp}; use gpui::prelude::FluentBuilder; use gpui::{ div, img, list, px, red, relative, rems, svg, white, AnyElement, App, AppContext, - ClipboardItem, Context, Element, Entity, EventEmitter, Flatten, FocusHandle, Focusable, - InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton, ObjectFit, - ParentElement, PathPromptOptions, Render, RetainAllImageCache, SharedString, - StatefulInteractiveElement, Styled, StyledImage, Subscription, Task, Window, + ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, + IntoElement, ListAlignment, ListOffset, ListState, MouseButton, ObjectFit, ParentElement, + PathPromptOptions, Render, RetainAllImageCache, SharedString, StatefulInteractiveElement, + Styled, StyledImage, Subscription, Task, WeakEntity, Window, }; use gpui_tokio::Tokio; use indexset::{BTreeMap, BTreeSet}; use itertools::Itertools; use nostr_sdk::prelude::*; -use person::PersonRegistry; +use person::{Person, PersonRegistry}; use settings::AppSettings; use smallvec::{smallvec, SmallVec}; use smol::fs; @@ -28,7 +27,6 @@ use ui::button::{Button, ButtonVariants}; use ui::context_menu::ContextMenuExt; use ui::dock_area::panel::{Panel, PanelEvent}; use ui::input::{InputEvent, InputState, TextInput}; -use ui::modal::ModalButtonProps; use ui::notification::Notification; use ui::popup_menu::PopupMenuExt; use ui::{ @@ -41,43 +39,54 @@ use crate::text::RenderedText; mod actions; mod emoji; -mod subject; mod text; -pub fn init(room: Entity, window: &mut Window, cx: &mut App) -> Entity { +pub fn init(room: WeakEntity, window: &mut Window, cx: &mut App) -> Entity { cx.new(|cx| ChatPanel::new(room, window, cx)) } +/// Chat Panel pub struct ChatPanel { - // Chat Room - room: Entity, - - // Messages - list_state: ListState, - messages: BTreeSet, - rendered_texts_by_id: BTreeMap, - reports_by_id: BTreeMap>, - - // New Message - input: Entity, - options: Entity, - replies_to: Entity>, - - // Media Attachment - attachments: Entity>, - uploading: bool, - - // Panel id: SharedString, focus_handle: FocusHandle, image_cache: Entity, - _subscriptions: SmallVec<[Subscription; 3]>, - _tasks: SmallVec<[Task<()>; 2]>, + /// Chat Room + room: WeakEntity, + + /// Message list state + list_state: ListState, + + /// All messages + messages: BTreeSet, + + /// Mapping message ids to their rendered texts + rendered_texts_by_id: BTreeMap, + + /// Mapping message ids to their reports + reports_by_id: BTreeMap>, + + /// Input state + input: Entity, + + /// Replies to + replies_to: Entity>, + + /// Media Attachment + attachments: Entity>, + + /// Upload state + uploading: bool, + + /// Async operations + tasks: SmallVec<[Task<()>; 2]>, + + /// Event subscriptions + _subscriptions: SmallVec<[Subscription; 2]>, } impl ChatPanel { - pub fn new(room: Entity, window: &mut Window, cx: &mut Context) -> Self { + pub fn new(room: WeakEntity, window: &mut Window, cx: &mut Context) -> Self { let input = cx.new(|cx| { InputState::new(window, cx) .placeholder("Message...") @@ -88,45 +97,64 @@ impl ChatPanel { let attachments = cx.new(|_| vec![]); let replies_to = cx.new(|_| HashSet::new()); - let options = cx.new(|_| SendOptions::default()); - let id = room.read(cx).id.to_string().into(); let messages = BTreeSet::from([Message::system()]); let list_state = ListState::new(messages.len(), ListAlignment::Bottom, px(1024.)); - let connect = room.read(cx).connect(cx); - let get_messages = room.read(cx).get_messages(cx); + let id: SharedString = room + .read_with(cx, |this, _cx| this.id.to_string().into()) + .unwrap_or("Unknown".into()); let mut subscriptions = smallvec![]; let mut tasks = smallvec![]; - tasks.push( - // Get messaging relays and encryption keys announcement for each member - cx.background_spawn(async move { - if let Err(e) = connect.await { - log::error!("Failed to initialize room: {}", e); - } - }), - ); + if let Ok(connect) = room.read_with(cx, |this, cx| this.connect(cx)) { + tasks.push( + // Get messaging relays and encryption keys announcement for each member + cx.background_spawn(async move { + if let Err(e) = connect.await { + log::error!("Failed to initialize room: {}", e); + } + }), + ); + }; - tasks.push( - // Load all messages belonging to this room - cx.spawn_in(window, async move |this, cx| { - let result = get_messages.await; + if let Ok(get_messages) = room.read_with(cx, |this, cx| this.get_messages(cx)) { + tasks.push( + // Load all messages belonging to this room + cx.spawn_in(window, async move |this, cx| { + let result = get_messages.await; - this.update_in(cx, |this, window, cx| { - match result { - Ok(events) => { - this.insert_messages(events, cx); + this.update_in(cx, |this, window, cx| { + match result { + Ok(events) => { + this.insert_messages(&events, cx); + } + Err(e) => { + window.push_notification(e.to_string(), cx); + } + }; + }) + .ok(); + }), + ); + } + + if let Some(room) = room.upgrade() { + subscriptions.push( + // Subscribe to room events + cx.subscribe_in(&room, window, move |this, _room, event, window, cx| { + match event { + RoomEvent::Incoming(message) => { + this.insert_message(message, false, cx); } - Err(e) => { - window.push_notification(e.to_string(), cx); + RoomEvent::Reload => { + this.load_messages(window, cx); } }; - }) - .ok(); - }), - ); + }), + ); + } subscriptions.push( // Subscribe to input events @@ -141,47 +169,6 @@ impl ChatPanel { ), ); - subscriptions.push( - // Subscribe to room events - cx.subscribe_in(&room, window, move |this, _, signal, window, cx| { - match signal { - RoomSignal::NewMessage((gift_wrap_id, event)) => { - let nostr = NostrRegistry::global(cx); - let tracker = nostr.read(cx).tracker(); - let gift_wrap_id = gift_wrap_id.to_owned(); - let message = Message::user(event.clone()); - - cx.spawn_in(window, async move |this, cx| { - let tracker = tracker.read().await; - - this.update_in(cx, |this, _window, cx| { - if !tracker.sent_ids().contains(&gift_wrap_id) { - this.insert_message(message, false, cx); - } - }) - .ok(); - }) - .detach(); - } - RoomSignal::Refresh => { - this.load_messages(window, cx); - } - }; - }), - ); - - subscriptions.push( - // Observe when user close chat panel - cx.on_release_in(window, move |this, window, cx| { - this.messages.clear(); - this.rendered_texts_by_id.clear(); - this.reports_by_id.clear(); - this.image_cache.update(cx, |this, cx| { - this.clear(window, cx); - }); - }), - ); - Self { id, messages, @@ -190,30 +177,26 @@ impl ChatPanel { input, replies_to, attachments, - options, rendered_texts_by_id: BTreeMap::new(), reports_by_id: BTreeMap::new(), uploading: false, image_cache: RetainAllImageCache::new(cx), focus_handle: cx.focus_handle(), _subscriptions: subscriptions, - _tasks: tasks, + tasks, } } /// Load all messages belonging to this room fn load_messages(&mut self, window: &mut Window, cx: &mut Context) { - let get_messages = self.room.read(cx).get_messages(cx); - - self._tasks.push( - // Run the task in the background - cx.spawn_in(window, async move |this, cx| { + if let Ok(get_messages) = self.room.read_with(cx, |this, cx| this.get_messages(cx)) { + self.tasks.push(cx.spawn_in(window, async move |this, cx| { let result = get_messages.await; this.update_in(cx, |this, window, cx| { match result { Ok(events) => { - this.insert_messages(events, cx); + this.insert_messages(&events, cx); } Err(e) => { window.push_notification(Notification::error(e.to_string()), cx); @@ -221,12 +204,13 @@ impl ChatPanel { }; }) .ok(); - }), - ); + })); + } } /// Get user input content and merged all attachments fn input_content(&self, cx: &Context) -> String { + // Get input's value let mut content = self.input.read(cx).value().trim().to_string(); // Get all attaches and merge its with message @@ -260,26 +244,20 @@ impl ChatPanel { return; } - // Temporary disable the message input - self.input.update(cx, |this, cx| { - this.set_loading(false, cx); - this.set_disabled(false, cx); - this.set_value("", window, cx); - }); + // Get the current room entity + let Some(room) = self.room.upgrade().map(|this| this.read(cx)) else { + return; + }; // Get replies_to if it's present let replies: Vec = self.replies_to.read(cx).iter().copied().collect(); - // Get the current room entity - let room = self.room.read(cx); - let opts = self.options.read(cx); - // Create a temporary message for optimistic update let rumor = room.create_message(&content, replies.as_ref(), cx); let rumor_id = rumor.id.unwrap(); // Create a task for sending the message in the background - let send_message = room.send_message(&rumor, opts, cx); + let send_message = room.send_message(&rumor, cx); // Optimistically update message list cx.spawn_in(window, async move |this, cx| { @@ -290,29 +268,32 @@ impl ChatPanel { // Update the message list and reset the states this.update_in(cx, |this, window, cx| { - this.insert_message(Message::user(rumor), true, cx); this.remove_all_replies(cx); this.remove_all_attachments(cx); + + // Reset the input to its default state this.input.update(cx, |this, cx| { this.set_loading(false, cx); this.set_disabled(false, cx); this.set_value("", window, cx); }); + + // Update the message list + this.insert_message(&rumor, true, cx); }) .ok(); }) .detach(); - self._tasks.push( - // Continue sending the message in the background - cx.spawn_in(window, async move |this, cx| { - let result = send_message.await; + self.tasks.push(cx.spawn_in(window, async move |this, cx| { + let result = send_message.await; - this.update_in(cx, |this, window, cx| { - match result { - Ok(reports) => { - // Update room's status - this.room.update(cx, |this, cx| { + this.update_in(cx, |this, window, cx| { + match result { + Ok(reports) => { + // Update room's status + this.room + .update(cx, |this, cx| { if this.kind != RoomKind::Ongoing { // Update the room kind to ongoing, // but keep the room kind if send failed @@ -321,50 +302,21 @@ impl ChatPanel { cx.notify(); } } - }); + }) + .ok(); - // Insert the sent reports - this.reports_by_id.insert(rumor_id, reports); + // Insert the sent reports + this.reports_by_id.insert(rumor_id, reports); - cx.notify(); - } - Err(e) => { - window.push_notification(e.to_string(), cx); - } + cx.notify(); } - }) - .ok(); - }), - ); - } - - /// Resend a failed message - #[allow(dead_code)] - fn resend_message(&mut self, id: &EventId, window: &mut Window, cx: &mut Context) { - if let Some(reports) = self.reports_by_id.get(id).cloned() { - let id_clone = id.to_owned(); - let resend = self.room.read(cx).resend_message(reports, cx); - - cx.spawn_in(window, async move |this, cx| { - let result = resend.await; - - this.update_in(cx, |this, window, cx| { - match result { - Ok(reports) => { - this.reports_by_id.entry(id_clone).and_modify(|this| { - *this = reports; - }); - cx.notify(); - } - Err(e) => { - window.push_notification(Notification::error(e.to_string()), cx); - } - }; - }) - .ok(); + Err(e) => { + window.push_notification(e.to_string(), cx); + } + } }) - .detach(); - } + .ok(); + })); } /// Insert a message into the chat panel @@ -390,21 +342,13 @@ impl ChatPanel { } /// Convert and insert a vector of nostr events into the chat panel - fn insert_messages(&mut self, events: Vec, cx: &mut Context) { - for event in events { - let m = Message::user(event); + fn insert_messages(&mut self, events: &[UnsignedEvent], cx: &mut Context) { + for event in events.iter() { // Bulk inserting messages, so no need to scroll to the latest message - self.insert_message(m, false, cx); + self.insert_message(event, false, cx); } } - /// Insert a warning message into the chat panel - #[allow(dead_code)] - fn insert_warning(&mut self, content: impl Into, cx: &mut Context) { - let m = Message::warning(content.into()); - self.insert_message(m, true, cx); - } - /// Check if a message failed to send by its ID fn is_sent_failed(&self, id: &EventId) -> bool { self.reports_by_id @@ -436,15 +380,6 @@ impl ChatPanel { }) } - fn profile(&self, public_key: &PublicKey, cx: &Context) -> Profile { - let persons = PersonRegistry::global(cx); - persons.read(cx).get_person(public_key, cx) - } - - fn signer_kind(&self, cx: &App) -> SignerKind { - self.options.read(cx).signer_kind - } - fn scroll_to(&self, id: EventId) { if let Some(ix) = self.messages.iter().position(|m| { if let Message::User(msg) = m { @@ -511,25 +446,19 @@ impl ChatPanel { Some(url) }); - if let Ok(task) = upload { + if let Ok(task) = upload.await { this.update(cx, |this, cx| { this.set_uploading(true, cx); }) .ok(); - let result = Flatten::flatten(task.await.map_err(|e| e.into())); - - this.update_in(cx, |this, window, cx| { - match result { - Ok(Some(url)) => { + this.update_in(cx, |this, _window, cx| { + match task { + Some(url) => { this.add_attachment(url, cx); this.set_uploading(false, cx); } - Ok(None) => { - this.set_uploading(false, cx); - } - Err(e) => { - window.push_notification(Notification::error(e.to_string()), cx); + None => { this.set_uploading(false, cx); } }; @@ -570,6 +499,11 @@ impl ChatPanel { }); } + fn profile(&self, public_key: &PublicKey, cx: &Context) -> Person { + let persons = PersonRegistry::global(cx); + persons.read(cx).get(public_key, cx) + } + fn render_announcement(&self, ix: usize, cx: &Context) -> AnyElement { v_flex() .id(ix) @@ -660,7 +594,6 @@ impl ChatPanel { text: AnyElement, cx: &Context, ) -> AnyElement { - let proxy = AppSettings::get_proxy_user_avatars(cx); let hide_avatar = AppSettings::get_hide_user_avatars(cx); let id = message.id; @@ -691,7 +624,7 @@ impl ChatPanel { this.child( div() .id(SharedString::from(format!("{ix}-avatar"))) - .child(Avatar::new(author.avatar(proxy)).size(rems(2.))) + .child(Avatar::new(author.avatar()).size(rems(2.))) .context_menu(move |this, _window, _cx| { let view = Box::new(OpenPublicKey(public_key)); let copy = Box::new(CopyPublicKey(public_key)); @@ -716,7 +649,7 @@ impl ChatPanel { div() .font_semibold() .text_color(cx.theme().text) - .child(author.display_name()), + .child(author.name()), ) .child(message.created_at.to_human_time()) .when_some(is_sent_success, |this, status| { @@ -773,7 +706,7 @@ impl ChatPanel { .child( div() .text_color(cx.theme().text_accent) - .child(author.display_name()), + .child(author.name()), ) .child( div() @@ -854,9 +787,9 @@ impl ChatPanel { fn render_report(report: &SendReport, cx: &App) -> impl IntoElement { let persons = PersonRegistry::global(cx); - let profile = persons.read(cx).get_person(&report.receiver, cx); - let name = profile.display_name(); - let avatar = profile.avatar(true); + let profile = persons.read(cx).get(&report.receiver, cx); + let name = profile.name(); + let avatar = profile.avatar(); v_flex() .gap_2() @@ -1116,7 +1049,7 @@ impl ChatPanel { fn render_reply(&self, id: &EventId, cx: &Context) -> impl IntoElement { if let Some(text) = self.message(id) { let persons = PersonRegistry::global(cx); - let profile = persons.read(cx).get_person(&text.author, cx); + let profile = persons.read(cx).get(&text.author, cx); div() .w_full() @@ -1139,7 +1072,7 @@ impl ChatPanel { .child( div() .text_color(cx.theme().text_accent) - .child(profile.display_name()), + .child(profile.name()), ), ) .child( @@ -1181,126 +1114,6 @@ impl ChatPanel { items } - - fn subject_button(&self, cx: &App) -> Button { - let room = self.room.downgrade(); - let subject = self - .room - .read(cx) - .subject - .as_ref() - .map(|subject| subject.to_string()); - - Button::new("subject") - .icon(IconName::Edit) - .tooltip("Change the subject of the conversation") - .on_click(move |_, window, cx| { - let view = subject::init(subject.clone(), window, cx); - let room = room.clone(); - let weak_view = view.downgrade(); - - window.open_modal(cx, move |this, _window, _cx| { - let room = room.clone(); - let weak_view = weak_view.clone(); - - this.confirm() - .title("Change the subject of the conversation") - .child(view.clone()) - .button_props(ModalButtonProps::default().ok_text("Change")) - .on_ok(move |_, _window, cx| { - if let Ok(subject) = - weak_view.read_with(cx, |this, cx| this.new_subject(cx)) - { - room.update(cx, |this, cx| { - this.set_subject(subject, cx); - }) - .ok(); - } - // true to close the modal - true - }) - }); - }) - } - - fn reload_button(&self, _cx: &App) -> Button { - let room = self.room.downgrade(); - - Button::new("reload") - .icon(IconName::Refresh) - .tooltip("Reload") - .on_click(move |_ev, window, cx| { - _ = room.update(cx, |this, cx| { - this.emit_refresh(cx); - window.push_notification("Reloaded", cx); - }); - }) - } - - fn on_open_seen_on(&mut self, ev: &SeenOn, window: &mut Window, cx: &mut Context) { - let id = ev.0; - let nostr = NostrRegistry::global(cx); - let client = nostr.read(cx).client(); - let tracker = nostr.read(cx).tracker(); - - let task: Task, Error>> = cx.background_spawn(async move { - let tracker = tracker.read().await; - let mut relays: Vec = vec![]; - - let filter = Filter::new() - .kind(Kind::ApplicationSpecificData) - .event(id) - .limit(1); - - if let Some(event) = client.database().query(filter).await?.first_owned() { - if let Some(Ok(id)) = event.tags.identifier().map(EventId::parse) { - if let Some(urls) = tracker.seen_on_relays.get(&id).cloned() { - relays.extend(urls); - } - } - } - - Ok(relays) - }); - - cx.spawn_in(window, async move |_, cx| { - if let Ok(urls) = task.await { - cx.update(|window, cx| { - window.open_modal(cx, move |this, _window, cx| { - this.show_close(true) - .title(SharedString::from("Seen on")) - .child(v_flex().pb_4().gap_2().children({ - let mut items = Vec::with_capacity(urls.len()); - - for url in urls.clone().into_iter() { - items.push( - h_flex() - .h_8() - .px_2() - .bg(cx.theme().elevated_surface_background) - .rounded(cx.theme().radius) - .font_semibold() - .text_xs() - .child(SharedString::from(url.to_string())), - ) - } - - items - })) - }); - }) - .ok(); - } - }) - .detach(); - } - - fn on_set_encryption(&mut self, ev: &SetSigner, _: &mut Window, cx: &mut Context) { - self.options.update(cx, move |this, cx| { - this.signer_kind = ev.0; - cx.notify(); - }); - } } impl Panel for ChatPanel { @@ -1309,24 +1122,18 @@ impl Panel for ChatPanel { } fn title(&self, cx: &App) -> AnyElement { - self.room.read_with(cx, |this, cx| { - let proxy = AppSettings::get_proxy_user_avatars(cx); - let label = this.display_name(cx); - let url = this.display_image(proxy, cx); + self.room + .read_with(cx, |this, cx| { + let label = this.display_name(cx); + let url = this.display_image(cx); - h_flex() - .gap_1p5() - .child(Avatar::new(url).size(rems(1.25))) - .child(label) - .into_any() - }) - } - - fn toolbar_buttons(&self, _window: &Window, cx: &App) -> Vec