diff --git a/Cargo.lock b/Cargo.lock index a9cceec6..3dae367e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" +checksum = "b1061f3ff92c2f65800df1f12fc7b4ff44ee14783104187dd04dfee6f11b0fd2" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -279,9 +279,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb42b2197bf15ccb092b62c74515dbd8b86d0effd934795f6687c93b6e679a2c" +checksum = "f658e2baef915ba0f26f1f7c42bfb8e12f532a01f449a090ded75ae7a07e9ba2" dependencies = [ "brotli", "flate2", @@ -293,9 +293,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" dependencies = [ "async-lock", "async-task", @@ -331,7 +331,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.23", + "rustix 0.37.25", "slab", "socket2 0.4.9", "waker-fn", @@ -348,9 +348,9 @@ dependencies = [ [[package]] name = "async-process" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf012553ce51eb7aa6dc2143804cc8252bd1cb681a1c5cb7fa94ca88682dee1d" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ "async-io", "async-lock", @@ -359,7 +359,7 @@ dependencies = [ "cfg-if", "event-listener 3.0.0", "futures-lite", - "rustix 0.38.14", + "rustix 0.38.19", "windows-sys 0.48.0", ] @@ -371,23 +371,22 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "async-signal" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99f3cb3f9ff89f7d718fbb942c9eb91bedff12e396adf09a622dfe7ffec2bc2" +checksum = "d2a5415b7abcdc9cd7d63d6badba5288b2ca017e3fbd4173b8f405449f1a2399" dependencies = [ "async-io", "async-lock", "atomic-waker", "cfg-if", - "concurrent-queue", "futures-core", "futures-io", - "libc", + "rustix 0.38.19", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -395,19 +394,19 @@ dependencies = [ [[package]] name = "async-task" -version = "4.4.1" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -557,9 +556,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" dependencies = [ "serde", ] @@ -609,9 +608,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" dependencies = [ "async-channel", "async-lock", @@ -625,9 +624,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.3.4" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -636,9 +635,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "da74e2b81409b1b743f8f0c62cc6254afefb8b8e50bbfe3735550f7aeefa3448" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -667,14 +666,14 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -1051,7 +1050,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.0", + "hashbrown 0.14.1", "lock_api", "once_cell", "parking_lot_core", @@ -1071,9 +1070,12 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -1164,7 +1166,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.0", + "libloading 0.8.1", ] [[package]] @@ -1327,7 +1329,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1338,7 +1340,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1365,25 +1367,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "2.3.1" @@ -1501,9 +1492,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -1648,7 +1639,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1904,7 +1895,7 @@ dependencies = [ [[package]] name = "gossip-relay-picker" version = "0.2.0-unstable" -source = "git+https://github.com/mikedilger/gossip-relay-picker?rev=2971fff6f3e72d5998dd9ecea9b029abcce86c2f#2971fff6f3e72d5998dd9ecea9b029abcce86c2f" +source = "git+https://github.com/mikedilger/gossip-relay-picker?rev=4f1ade0e491889544c331633df9a275f59ccaffd#4f1ade0e491889544c331633df9a275f59ccaffd" dependencies = [ "async-trait", "dashmap", @@ -1953,9 +1944,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" dependencies = [ "ahash 0.8.3", "allocator-api2", @@ -1967,7 +1958,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.1", ] [[package]] @@ -1975,7 +1966,7 @@ name = "heed" version = "0.20.0-alpha.4" source = "git+https://github.com/meilisearch/heed?rev=02030e3bf3d26ee98d4f5343fc086a7b63289159#02030e3bf3d26ee98d4f5343fc086a7b63289159" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "bytemuck", "byteorder", "heed-traits", @@ -2133,16 +2124,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -2201,12 +2192,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.1", ] [[package]] @@ -2287,9 +2278,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -2356,9 +2347,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libloading" @@ -2372,9 +2363,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -2382,9 +2373,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libsqlite3-sys" @@ -2437,9 +2428,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lmdb-master-sys" @@ -2454,9 +2445,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -2497,9 +2488,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -2730,7 +2721,7 @@ dependencies = [ [[package]] name = "nostr-types" version = "0.7.0-unstable" -source = "git+https://github.com/mikedilger/nostr-types?rev=976bbba43404ee9130a6f459bdca4564e9355c8c#976bbba43404ee9130a6f459bdca4564e9355c8c" +source = "git+https://github.com/mikedilger/nostr-types?rev=321e8b778f632fe084d45b5be9062e88b9742f92#321e8b778f632fe084d45b5be9062e88b9742f92" dependencies = [ "aes", "base64", @@ -2797,9 +2788,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -2853,7 +2844,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2938,7 +2929,7 @@ version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -2955,7 +2946,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3028,9 +3019,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -3044,13 +3035,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -3126,7 +3117,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3213,6 +3204,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3237,9 +3234,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -3351,6 +3348,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -3364,14 +3370,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -3385,13 +3391,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -3402,15 +3408,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "async-compression", "base64", @@ -3437,6 +3443,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", @@ -3483,7 +3490,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206cee941730eaf90a22c84235b25193df661393688162e15551164f92f09eca" dependencies = [ "ahash 0.8.3", - "bitflags 2.4.0", + "bitflags 2.4.1", "instant", "num-traits", "once_cell", @@ -3500,7 +3507,7 @@ checksum = "853977598f084a492323fe2f7896b4100a86284ee8473612de60021ea341310f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3513,11 +3520,25 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce3045ffa7c981a6ee93f640b538952e155f1ae3a1a02b84547fc7a56b7059a" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ringbuf" version = "0.3.3" @@ -3534,7 +3555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64", - "bitflags 2.4.0", + "bitflags 2.4.1", "serde", "serde_derive", ] @@ -3575,7 +3596,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "chrono", "fallible-iterator", "fallible-streaming-iterator", @@ -3608,9 +3629,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.23" +version = "0.37.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" dependencies = [ "bitflags 1.3.2", "errno", @@ -3622,14 +3643,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.7", + "linux-raw-sys 0.4.10", "windows-sys 0.48.0", ] @@ -3640,7 +3661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", - "ring", + "ring 0.16.20", "rustls-webpki 0.101.6", "sct", ] @@ -3660,8 +3681,8 @@ version = "0.100.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -3670,8 +3691,8 @@ version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -3759,8 +3780,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -3863,28 +3884,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3906,7 +3927,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3945,9 +3966,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -4088,7 +4109,7 @@ source = "git+https://github.com/mikedilger/speedy?rev=b8b713a7006958616dd3ef3ba dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4156,9 +4177,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -4174,6 +4195,27 @@ dependencies = [ "crossbeam-queue", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.8.0" @@ -4183,7 +4225,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.3.5", - "rustix 0.38.14", + "rustix 0.38.19", "windows-sys 0.48.0", ] @@ -4204,7 +4246,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4244,12 +4286,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -4356,9 +4399,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -4381,7 +4424,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4447,7 +4490,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.1", + "indexmap 2.0.2", "toml_datetime", "winnow", ] @@ -4460,11 +4503,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4472,20 +4514,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -4644,6 +4686,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" @@ -4804,7 +4852,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -4838,7 +4886,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4946,20 +4994,19 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19353897b48e2c4d849a2d73cb0aeb16dc2be4e00c565abfc11eb65a806e47de" +checksum = "8208e3fdbc243c8fd30805721869242a7f6de3e2e9f3b057652ab36e52ae1e87" dependencies = [ "js-sys", - "once_cell", "wasm-bindgen", ] [[package]] name = "webbrowser" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2c79b77f525a2d670cb40619d7d9c673d09e0666f72c591ebd7861f84a87e57" +checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" dependencies = [ "core-foundation", "home", @@ -4974,12 +5021,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.4", + "untrusted 0.9.0", ] [[package]] @@ -5054,6 +5101,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-implement" version = "0.48.0" @@ -5245,9 +5301,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" dependencies = [ "memchr", ] diff --git a/docs/DEVELOPING.md b/docs/DEVELOPING.md index ed9718b7..40ab6223 100644 --- a/docs/DEVELOPING.md +++ b/docs/DEVELOPING.md @@ -1,11 +1,13 @@ # Developing +![Gossip Architecture](./assets/architecture.png) + Gossip is architected with the following components: - A User Interface thread, synchronous - Tokio asynchronous runtime running - - An overlord (handles most jobs) - - A set of minions (each one handles one relay) + - An overlord: handles any operation that involves talking to relays, and a few more + - A set of minions: each one contacts a proper relay to get data, composing the filter and sending it to the relay ## Keeping the UI responsive @@ -48,7 +50,7 @@ The flow generally happens like this: ## Pull Requests -I prefer that you run and make pass: +Before issuing a Pull Request, please run and make pass: ````bash cargo clippy @@ -60,6 +62,4 @@ and then cargo fmt ```` -before you issue a pull request. Otherwise I'll have to do it for you. - -Also, I don't like branches that have a lot of commits that are messed up, but happen to end up in a good state due to the last commit. If you have a branch like this, create a new branch and one-by-one create a series of commits, each one a single logical step, each one compiling, each one passing clippy and rustfmt, each one documented, and each one doing something useful, such that this series of commits ends up where you originally got to (or somewhere even better). This not only makes it much easier to evaluate the PR, but it makes it possible to revert logical units later on if anything needs to be reverted, without necessarily reverting the entire branch. +Avoid branches that have a lot of commits that are messed up, but happen to end up in a good state due to the last commit. If you have a branch like this, create a new branch and one-by-one create a series of commits, each one a single logical step, each one compiling, each one passing clippy and rustfmt, each one documented, and each one doing something useful, such that this series of commits ends up where you originally got to (or somewhere even better). This not only makes it much easier to evaluate the PR, but it makes it possible to revert logical units later on if anything needs to be reverted, without necessarily reverting the entire branch. diff --git a/docs/assets/architecture.png b/docs/assets/architecture.png new file mode 100644 index 00000000..bb312b11 Binary files /dev/null and b/docs/assets/architecture.png differ diff --git a/docs/assets/gossip_architecture.drawio b/docs/assets/gossip_architecture.drawio new file mode 100644 index 00000000..e3ccb2e0 --- /dev/null +++ b/docs/assets/gossip_architecture.drawio @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gossip-bin/Cargo.toml b/gossip-bin/Cargo.toml index caf06ce9..b8e2ec40 100644 --- a/gossip-bin/Cargo.toml +++ b/gossip-bin/Cargo.toml @@ -21,13 +21,13 @@ bech32 = "0.9" eframe = { git = "https://github.com/mikedilger/egui", rev = "50393e4f34ac6246b8c2424e42fbe5b95e4b4452", features = [ "persistence", "wayland" ] } egui-winit = { git = "https://github.com/mikedilger/egui", rev = "50393e4f34ac6246b8c2424e42fbe5b95e4b4452", features = [ "default" ] } egui-video = { git = "https://github.com/mikedilger/egui-video", rev = "81cc3ee58818754272582397161cc55ff11bde18", features = [ "from_bytes" ], optional = true } -gossip-relay-picker = { git = "https://github.com/mikedilger/gossip-relay-picker", rev = "2971fff6f3e72d5998dd9ecea9b029abcce86c2f" } +gossip-relay-picker = { git = "https://github.com/mikedilger/gossip-relay-picker", rev = "4f1ade0e491889544c331633df9a275f59ccaffd" } gossip-lib = { path = "../gossip-lib" } humansize = "2.1" image = { version = "0.24.6", features = [ "png", "jpeg" ] } lazy_static = "1.4" memoize = "0.4" -nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "976bbba43404ee9130a6f459bdca4564e9355c8c", features = [ "speedy" ] } +nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "321e8b778f632fe084d45b5be9062e88b9742f92", features = [ "speedy" ] } qrcode = { git = "https://github.com/mikedilger/qrcode-rust", rev = "519b77b3efa3f84961169b47d3de08c5ddd86548" } resvg = "0.35.0" rpassword = "7.2" diff --git a/gossip-bin/src/commands.rs b/gossip-bin/src/commands.rs index 016c7fcd..d2a6ee09 100644 --- a/gossip-bin/src/commands.rs +++ b/gossip-bin/src/commands.rs @@ -438,7 +438,9 @@ pub fn print_relays(_cmd: Command) -> Result<(), Error> { } pub fn print_followed(_cmd: Command) -> Result<(), Error> { - let pubkeys = GLOBALS.storage.get_people_in_list(PersonList::Followed)?; + let pubkeys = GLOBALS + .storage + .get_people_in_list(PersonList::Followed, None)?; for pk in &pubkeys { println!("{}", pk.as_hex_string()); } @@ -446,7 +448,9 @@ pub fn print_followed(_cmd: Command) -> Result<(), Error> { } pub fn print_muted(_cmd: Command) -> Result<(), Error> { - let pubkeys = GLOBALS.storage.get_people_in_list(PersonList::Muted)?; + let pubkeys = GLOBALS + .storage + .get_people_in_list(PersonList::Muted, None)?; for pk in &pubkeys { println!("{}", pk.as_hex_string()); } diff --git a/gossip-bin/src/ui/feed/mod.rs b/gossip-bin/src/ui/feed/mod.rs index 21f72aa2..0a181575 100644 --- a/gossip-bin/src/ui/feed/mod.rs +++ b/gossip-bin/src/ui/feed/mod.rs @@ -52,16 +52,20 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram let feed_kind = GLOBALS.feed.get_feed_kind(); match feed_kind { - FeedKind::Followed(with_replies) => { + FeedKind::List(list, with_replies) => { let feed = GLOBALS.feed.get_followed(); - let id = if with_replies { "main" } else { "general" }; + let id = format!( + "{} {}", + Into::::into(list), + if with_replies { "main" } else { "general" } + ); ui.add_space(10.0); ui.allocate_ui_with_layout( Vec2::new(ui.available_width(), ui.spacing().interact_size.y), egui::Layout::left_to_right(egui::Align::Center), |ui| { add_left_space(ui); - ui.heading("Main feed"); + ui.heading(list.name()); recompute_btn(app, ui); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { @@ -75,7 +79,8 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram ) .clicked() { - app.set_page(Page::Feed(FeedKind::Followed( + app.set_page(Page::Feed(FeedKind::List( + list, app.mainfeed_include_nonroot, ))); ctx.data_mut(|d| { @@ -90,7 +95,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram }, ); ui.add_space(6.0); - render_a_feed(app, ctx, frame, ui, feed, false, id); + render_a_feed(app, ctx, frame, ui, feed, false, &id); } FeedKind::Inbox(indirect) => { if app.settings.public_key.is_none() { diff --git a/gossip-bin/src/ui/feed/notedata.rs b/gossip-bin/src/ui/feed/notedata.rs index 1a44d7e1..ca61a9a2 100644 --- a/gossip-bin/src/ui/feed/notedata.rs +++ b/gossip-bin/src/ui/feed/notedata.rs @@ -1,5 +1,6 @@ use gossip_lib::GLOBALS; use gossip_lib::{Person, PersonList}; +use std::collections::HashMap; use nostr_types::{ ContentSegment, Event, EventDelegation, EventKind, Id, MilliSatoshi, NostrBech32, PublicKey, @@ -27,7 +28,7 @@ pub(super) struct NoteData { /// Author of this note (considers delegation) pub(super) author: Person, /// Lists the author is on - pub(super) lists: Vec, + pub(super) lists: HashMap, /// Deletion reason if any pub(super) deletion: Option, /// Do we consider this note as being a repost of another? @@ -197,7 +198,7 @@ impl NoteData { let lists = match GLOBALS.storage.read_person_lists(&author_pubkey) { Ok(lists) => lists, - _ => vec![], + _ => HashMap::new(), }; NoteData { @@ -232,10 +233,10 @@ impl NoteData { #[allow(dead_code)] pub(super) fn followed(&self) -> bool { - self.lists.contains(&PersonList::Followed) + self.lists.contains_key(&PersonList::Followed) } pub(super) fn muted(&self) -> bool { - self.lists.contains(&PersonList::Muted) + self.lists.contains_key(&PersonList::Muted) } } diff --git a/gossip-bin/src/ui/help/mod.rs b/gossip-bin/src/ui/help/mod.rs index fcd6f520..040f1862 100644 --- a/gossip-bin/src/ui/help/mod.rs +++ b/gossip-bin/src/ui/help/mod.rs @@ -1,7 +1,7 @@ use super::{GossipUi, Page}; use eframe::egui; use egui::{Context, Ui}; -use gossip_lib::FeedKind; +use gossip_lib::{FeedKind, PersonList}; mod about; mod stats; @@ -103,7 +103,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra ui.horizontal_wrapped(|ui| { ui.label("On the"); if ui.link("Feed > Following").clicked() { - app.set_page(Page::Feed(FeedKind::Followed(app.mainfeed_include_nonroot))); + app.set_page(Page::Feed(FeedKind::List(PersonList::Followed, app.mainfeed_include_nonroot))); } ui.label("page, enjoy the feed."); }); @@ -199,7 +199,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra ui.horizontal_wrapped(|ui| { ui.label("On the"); if ui.link("Feed > Following").clicked() { - app.set_page(Page::Feed(FeedKind::Followed(app.mainfeed_include_nonroot))); + app.set_page(Page::Feed(FeedKind::List(PersonList::Followed, app.mainfeed_include_nonroot))); } ui.label("page, enjoy the feed."); }); diff --git a/gossip-bin/src/ui/mod.rs b/gossip-bin/src/ui/mod.rs index ac0e52a2..9e78c788 100644 --- a/gossip-bin/src/ui/mod.rs +++ b/gossip-bin/src/ui/mod.rs @@ -42,7 +42,7 @@ use gossip_lib::Settings; use gossip_lib::{DmChannel, DmChannelData}; use gossip_lib::{Person, PersonList}; use gossip_lib::{ZapState, GLOBALS}; -use nostr_types::{Id, IdHex, Metadata, MilliSatoshi, Profile, PublicKey, UncheckedUrl, Url}; +use nostr_types::{Id, Metadata, MilliSatoshi, Profile, PublicKey, UncheckedUrl, Url}; use std::collections::{HashMap, HashSet}; #[cfg(feature = "video-ffmpeg")] use std::rc::Rc; @@ -550,7 +550,7 @@ impl GossipUi { None => (false, dpi), }; - let mut start_page = Page::Feed(FeedKind::Followed(false)); + let mut start_page = Page::Feed(FeedKind::List(PersonList::Followed, false)); // Possibly enter the wizard instead let mut wizard_state: WizardState = Default::default(); @@ -683,8 +683,8 @@ impl GossipUi { fn set_page_inner(&mut self, page: Page) { // Setting the page often requires some associated actions: match &page { - Page::Feed(FeedKind::Followed(with_replies)) => { - GLOBALS.feed.set_feed_to_followed(*with_replies); + Page::Feed(FeedKind::List(list, with_replies)) => { + GLOBALS.feed.set_feed_to_main(*list, *with_replies); } Page::Feed(FeedKind::Inbox(indirect)) => { GLOBALS.feed.set_feed_to_inbox(*indirect); @@ -763,8 +763,8 @@ impl GossipUi { ui.separator(); ui.add_space(4.0); - if self.add_selected_label(ui, matches!(self.page, Page::Feed(FeedKind::Followed(_))), "Main Feed").clicked() { - self.set_page(Page::Feed(FeedKind::Followed(self.mainfeed_include_nonroot))); + if self.add_selected_label(ui, matches!(self.page, Page::Feed(FeedKind::List(PersonList::Followed, _))), "Main Feed").clicked() { + self.set_page(Page::Feed(FeedKind::List(PersonList::Followed, self.mainfeed_include_nonroot))); } if let Some(pubkey) = GLOBALS.signer.public_key() { if self.add_selected_label(ui, matches!(&self.page, Page::Feed(FeedKind::Person(key)) if *key == pubkey), "My Notes").clicked() { @@ -1167,16 +1167,16 @@ impl GossipUi { } } if !followed && ui.button("Follow").clicked() { - let _ = GLOBALS.people.follow(&person.pubkey, true); + let _ = GLOBALS.people.follow(&person.pubkey, true, true); } else if followed && ui.button("Unfollow").clicked() { - let _ = GLOBALS.people.follow(&person.pubkey, false); + let _ = GLOBALS.people.follow(&person.pubkey, false, true); } // Do not show 'Mute' if this is yourself if muted || !is_self { let mute_label = if muted { "Unmute" } else { "Mute" }; if ui.button(mute_label).clicked() { - let _ = GLOBALS.people.mute(&person.pubkey, !muted); + let _ = GLOBALS.people.mute(&person.pubkey, !muted, true); app.notes.cache_invalidate_person(&person.pubkey); } } @@ -1497,7 +1497,7 @@ impl GossipUi { "VISIBLE = {:?}", self.visible_note_ids .iter() - .map(|id| Into::::into(*id).prefix(10).into_string()) + .map(|id| id.as_hex_string().as_str().get(0..10).unwrap().to_owned()) .collect::>() ); diff --git a/gossip-bin/src/ui/people/follow.rs b/gossip-bin/src/ui/people/follow.rs index 5dbeab7c..7a30801b 100644 --- a/gossip-bin/src/ui/people/follow.rs +++ b/gossip-bin/src/ui/people/follow.rs @@ -38,19 +38,20 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr if let Ok(pubkey) = PublicKey::try_from_bech32_string(app.follow_someone.trim(), true) { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::FollowPubkey(pubkey)); + .send(ToOverlordMessage::FollowPubkey(pubkey, true)); } else if let Ok(pubkey) = PublicKey::try_from_hex_string(app.follow_someone.trim(), true) { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::FollowPubkey(pubkey)); + .send(ToOverlordMessage::FollowPubkey(pubkey, true)); } else if let Ok(profile) = Profile::try_from_bech32_string(app.follow_someone.trim(), true) { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::FollowNprofile(profile)); + .send(ToOverlordMessage::FollowNprofile(profile, true)); } else if gossip_lib::nip05::parse_nip05(app.follow_someone.trim()).is_ok() { let _ = GLOBALS.to_overlord.send(ToOverlordMessage::FollowNip05( app.follow_someone.trim().to_owned(), + true, )); } else { GLOBALS diff --git a/gossip-bin/src/ui/people/followed.rs b/gossip-bin/src/ui/people/followed.rs index 64ff3fad..2a47b674 100644 --- a/gossip-bin/src/ui/people/followed.rs +++ b/gossip-bin/src/ui/people/followed.rs @@ -112,7 +112,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::RefreshFollowedMetadata); + .send(ToOverlordMessage::RefreshSubscribedMetadata); } }); diff --git a/gossip-bin/src/ui/people/muted.rs b/gossip-bin/src/ui/people/muted.rs index cf9fd7e9..17f3d0f6 100644 --- a/gossip-bin/src/ui/people/muted.rs +++ b/gossip-bin/src/ui/people/muted.rs @@ -157,7 +157,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra GossipUi::render_person_name_line(app, ui, person, false); if ui.button("UNMUTE").clicked() { - let _ = GLOBALS.people.mute(&person.pubkey, false); + let _ = GLOBALS.people.mute(&person.pubkey, false, true); } }); }); diff --git a/gossip-bin/src/ui/widgets/mod.rs b/gossip-bin/src/ui/widgets/mod.rs index 652f4694..09b6b8d4 100644 --- a/gossip-bin/src/ui/widgets/mod.rs +++ b/gossip-bin/src/ui/widgets/mod.rs @@ -86,12 +86,10 @@ pub(super) fn set_important_button_visuals(ui: &mut Ui, app: &GossipUi) { let visuals = ui.visuals_mut(); visuals.widgets.inactive.weak_bg_fill = app.theme.accent_color(); visuals.widgets.inactive.fg_stroke.width = 1.0; - visuals.widgets.inactive.fg_stroke.color = - app.theme.get_style().visuals.extreme_bg_color; + visuals.widgets.inactive.fg_stroke.color = app.theme.get_style().visuals.extreme_bg_color; visuals.widgets.hovered.weak_bg_fill = app.theme.navigation_text_color(); visuals.widgets.hovered.fg_stroke.color = app.theme.accent_color(); - visuals.widgets.inactive.fg_stroke.color = - app.theme.get_style().visuals.extreme_bg_color; + visuals.widgets.inactive.fg_stroke.color = app.theme.get_style().visuals.extreme_bg_color; } // /// UTF-8 safe truncate (String::truncate() can panic) diff --git a/gossip-bin/src/ui/widgets/relay_entry.rs b/gossip-bin/src/ui/widgets/relay_entry.rs index 6a81ad7f..a94cebdd 100644 --- a/gossip-bin/src/ui/widgets/relay_entry.rs +++ b/gossip-bin/src/ui/widgets/relay_entry.rs @@ -357,7 +357,15 @@ impl RelayEntry { let is_personal = self.relay.usage_bits != 0; let id = self.make_id("remove_link"); let text = "Remove from personal list"; - let response = draw_link_at(ui, id, pos, text.into(), Align::Min, self.enabled && is_personal, true); + let response = draw_link_at( + ui, + id, + pos, + text.into(), + Align::Min, + self.enabled && is_personal, + true, + ); if response.clicked() { let _ = GLOBALS.storage.modify_relay( &self.relay.url, @@ -379,15 +387,8 @@ impl RelayEntry { let id = self.make_id("disconnect_link"); let text = "Force disconnect"; let can_disconnect = self.enabled && self.connected; - let disconnect_response = draw_link_at( - ui, - id, - pos, - text.into(), - Align::Min, - can_disconnect, - true, - ); + let disconnect_response = + draw_link_at(ui, id, pos, text.into(), Align::Min, can_disconnect, true); if can_disconnect && disconnect_response.clicked() { let _ = GLOBALS .to_overlord diff --git a/gossip-bin/src/ui/wizard/follow_people.rs b/gossip-bin/src/ui/wizard/follow_people.rs index 3fe20165..6ae0f0a4 100644 --- a/gossip-bin/src/ui/wizard/follow_people.rs +++ b/gossip-bin/src/ui/wizard/follow_people.rs @@ -3,9 +3,7 @@ use crate::ui::{GossipUi, Page}; use eframe::egui; use egui::{Context, RichText, Ui}; use gossip_lib::comms::ToOverlordMessage; -use gossip_lib::FeedKind; -use gossip_lib::Person; -use gossip_lib::GLOBALS; +use gossip_lib::{FeedKind, Person, PersonList, GLOBALS}; use gossip_relay_picker::Direction; use nostr_types::{Profile, PublicKey}; @@ -96,22 +94,23 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr if let Ok(pubkey) = PublicKey::try_from_bech32_string(app.follow_someone.trim(), true) { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::FollowPubkey(pubkey)); + .send(ToOverlordMessage::FollowPubkey(pubkey, true)); } else if let Ok(pubkey) = PublicKey::try_from_hex_string(app.follow_someone.trim(), true) { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::FollowPubkey(pubkey)); + .send(ToOverlordMessage::FollowPubkey(pubkey, true)); } else if let Ok(profile) = Profile::try_from_bech32_string(app.follow_someone.trim(), true) { let _ = GLOBALS .to_overlord - .send(ToOverlordMessage::FollowNprofile(profile)); + .send(ToOverlordMessage::FollowNprofile(profile, true)); } else if gossip_lib::nip05::parse_nip05(app.follow_someone.trim()).is_ok() { let _ = GLOBALS.to_overlord.send(ToOverlordMessage::FollowNip05( app.follow_someone.trim().to_owned(), + true, )); } else { app.wizard_state.error = Some("ERROR: Invalid pubkey".to_owned()); @@ -143,7 +142,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PushFollow); let _ = GLOBALS.storage.write_wizard_complete(true, None); - app.page = Page::Feed(FeedKind::Followed(false)); + app.page = Page::Feed(FeedKind::List(PersonList::Followed, false)); } ui.add_space(20.0); @@ -153,7 +152,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr } if ui.button(label).clicked() { let _ = GLOBALS.storage.write_wizard_complete(true, None); - app.page = Page::Feed(FeedKind::Followed(false)); + app.page = Page::Feed(FeedKind::List(PersonList::Followed, false)); } } else { ui.add_space(20.0); @@ -161,7 +160,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr label = label.color(app.theme.accent_color()); if ui.button(label).clicked() { let _ = GLOBALS.storage.write_wizard_complete(true, None); - app.page = Page::Feed(FeedKind::Followed(false)); + app.page = Page::Feed(FeedKind::List(PersonList::Followed, false)); } } } diff --git a/gossip-bin/src/ui/wizard/mod.rs b/gossip-bin/src/ui/wizard/mod.rs index 6633e664..8b1c7360 100644 --- a/gossip-bin/src/ui/wizard/mod.rs +++ b/gossip-bin/src/ui/wizard/mod.rs @@ -2,9 +2,7 @@ use crate::ui::{GossipUi, Page}; use eframe::egui; use egui::widgets::{Button, Slider}; use egui::{Align, Context, Layout}; -use gossip_lib::FeedKind; -use gossip_lib::Relay; -use gossip_lib::GLOBALS; +use gossip_lib::{FeedKind, PersonList, Relay, GLOBALS}; mod follow_people; mod import_keys; @@ -23,23 +21,23 @@ static DEFAULT_RELAYS: [&str; 20] = [ "wss://nostr.einundzwanzig.space/", "wss://nostr.mutinywallet.com/", "wss://relay.nostrplebs.com/", - "wss://christpill.nostr1.com/", "wss://nostr-pub.wellorder.net/", "wss://relay.damus.io/", - "wss://bevo.nostr1.com/", - "wss://relay.snort.social/", "wss://public.relaying.io/", "wss://nostrue.com/", + "wss://relay.snort.social/", "wss://relay.noswhere.com/", "wss://relay.primal.net/", "wss://relay.nostr.jabber.ch/", - "wss://relay.nostr.band/", "wss://relay.wellorder.net/", - "wss://nostr.coinfundit.com/", - "wss://relay.nostrich.de/", - "wss://verbiricha.nostr1.com/", + "wss://relay.nostr.band/", "wss://nostr21.com/", + "wss://relayable.org/", "wss://nostr.bitcoiner.social/", + "wss://offchain.pub/", + "wss://nostr.coinfundit.com/", + "wss://nstr.pjv.me/", + "wss://welcome.nostr.wine/", ]; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -201,7 +199,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram if wp != WizardPage::FollowPeople { if ui.button(" X Exit this Wizard").clicked() { let _ = GLOBALS.storage.write_wizard_complete(true, None); - app.page = Page::Feed(FeedKind::Followed(false)); + app.page = Page::Feed(FeedKind::List(PersonList::Followed, false)); } } diff --git a/gossip-bin/src/ui/wizard/setup_relays.rs b/gossip-bin/src/ui/wizard/setup_relays.rs index 140d77cc..a78e0dbb 100644 --- a/gossip-bin/src/ui/wizard/setup_relays.rs +++ b/gossip-bin/src/ui/wizard/setup_relays.rs @@ -53,22 +53,12 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr .cloned() .collect(); - let mut discovery_relays: Vec = relays + let discovery_relays: Vec = relays .iter() .filter(|relay| relay.has_usage_bits(Relay::DISCOVER)) .cloned() .collect(); - if !discovery_relays - .iter() - .any(|r| r.url.as_str() == "wss://purplepag.es/") - { - let mut purple_pages = read_relay(&RelayUrl::try_from_str("wss://purplepag.es/").unwrap()); - purple_pages.set_usage_bits(Relay::DISCOVER); - let _ = GLOBALS.storage.write_relay(&purple_pages, None); - discovery_relays.push(purple_pages); - } - let mut need_more = false; ui.add_space(20.0); @@ -141,7 +131,9 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr for relay in discovery_relays.iter() { ui.horizontal(|ui| { if ui.button("🗑").clicked() { - unimplemented!(); + let mut r = relay.clone(); + r.clear_usage_bits(Relay::DISCOVER | Relay::ADVERTISE); + let _ = GLOBALS.storage.write_relay(&r, None); } ui.label(relay.url.as_str()); }); diff --git a/gossip-lib/Cargo.toml b/gossip-lib/Cargo.toml index a02b690c..1c06c3d2 100644 --- a/gossip-lib/Cargo.toml +++ b/gossip-lib/Cargo.toml @@ -27,7 +27,7 @@ fallible-iterator = "0.2" filetime = "0.2" futures = "0.3" futures-util = "0.3" -gossip-relay-picker = { git = "https://github.com/mikedilger/gossip-relay-picker", rev = "2971fff6f3e72d5998dd9ecea9b029abcce86c2f" } +gossip-relay-picker = { git = "https://github.com/mikedilger/gossip-relay-picker", rev = "4f1ade0e491889544c331633df9a275f59ccaffd" } heed = { git = "https://github.com/meilisearch/heed", rev = "02030e3bf3d26ee98d4f5343fc086a7b63289159" } hex = "0.4" http = "0.2" @@ -36,7 +36,7 @@ kamadak-exif = "0.5" lazy_static = "1.4" linkify = "0.9" mime = "0.3" -nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "976bbba43404ee9130a6f459bdca4564e9355c8c", features = [ "speedy" ] } +nostr-types = { git = "https://github.com/mikedilger/nostr-types", rev = "321e8b778f632fe084d45b5be9062e88b9742f92", features = [ "speedy" ] } parking_lot = "0.12" paste = "1.0" rand = "0.8" diff --git a/gossip-lib/src/comms.rs b/gossip-lib/src/comms.rs index 07042beb..b450bc19 100644 --- a/gossip-lib/src/comms.rs +++ b/gossip-lib/src/comms.rs @@ -52,13 +52,13 @@ pub enum ToOverlordMessage { FetchEventAddr(EventAddr), /// Calls [follow_pubkey](crate::Overlord::follow_pubkey) - FollowPubkey(PublicKey), + FollowPubkey(PublicKey, bool), /// Calls [follow_nip05](crate::Overlord::follow_nip05) - FollowNip05(String), + FollowNip05(String, bool), /// Calls [follow_nprofile](crate::Overlord::follow_nprofile) - FollowNprofile(Profile), + FollowNprofile(Profile, bool), /// Calls [generate_private_key](crate::Overlord::generate_private_key) GeneratePrivateKey(String), @@ -79,9 +79,6 @@ pub enum ToOverlordMessage { /// Calls [like](crate::Overlord::like) Like(Id, PublicKey), - /// internal (minions use this channel too) - MinionIsReady, - /// internal (minions use this channel too) MinionJobComplete(RelayUrl, u64), @@ -120,8 +117,8 @@ pub enum ToOverlordMessage { /// internal (the overlord sends messages to itself sometimes!) ReengageMinion(RelayUrl, Vec), - /// Calls [reresh_followed_metadata](crate::Overlord::refresh_followed_metadata) - RefreshFollowedMetadata, + /// Calls [reresh_subscribed_metadata](crate::Overlord::refresh_subscribed_metadata) + RefreshSubscribedMetadata, /// Calls [repost](crate::Overlord::repost) Repost(Id), diff --git a/gossip-lib/src/feed.rs b/gossip-lib/src/feed.rs index 4a14d9a9..8c2d1d9e 100644 --- a/gossip-lib/src/feed.rs +++ b/gossip-lib/src/feed.rs @@ -2,6 +2,7 @@ use crate::comms::{ToMinionMessage, ToMinionPayload, ToMinionPayloadDetail, ToOv use crate::dm_channel::DmChannel; use crate::error::Error; use crate::globals::GLOBALS; +use crate::people::PersonList; use nostr_types::{Event, EventKind, Id, PublicKey, PublicKeyHex, RelayUrl, Unixtime}; use parking_lot::RwLock; use std::collections::HashSet; @@ -12,8 +13,8 @@ use tokio::task; /// Kinds of feeds, with configuration parameteers #[derive(Clone, Debug, PartialEq, Eq)] pub enum FeedKind { - Followed(bool), // with replies - Inbox(bool), // indirect + List(PersonList, bool), // with replies + Inbox(bool), // indirect Thread { id: Id, referenced_by: Id, @@ -27,7 +28,7 @@ impl std::fmt::Display for FeedKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FeedKind::DmChat(channel) => write!(f, "{}", channel.name()), - FeedKind::Followed(_) => write!(f, "Following"), + FeedKind::List(pl, _) => write!(f, "{}", pl.name()), FeedKind::Inbox(_) => write!(f, "Inbox"), FeedKind::Thread { id: _, @@ -69,7 +70,7 @@ impl Feed { pub(crate) fn new() -> Feed { Feed { recompute_lock: AtomicBool::new(false), - current_feed_kind: RwLock::new(FeedKind::Followed(false)), + current_feed_kind: RwLock::new(FeedKind::List(PersonList::Followed, false)), followed_feed: RwLock::new(Vec::new()), inbox_feed: RwLock::new(Vec::new()), person_feed: RwLock::new(Vec::new()), @@ -108,12 +109,12 @@ impl Feed { } } - /// Change the feed to the main `followed` feed - pub fn set_feed_to_followed(&self, with_replies: bool) { + /// Change the feed to the main feed + pub fn set_feed_to_main(&self, list: PersonList, with_replies: bool) { // We are always subscribed to the general feed. Don't resubscribe here // because it won't have changed, but the relays will shower you with // all those events again. - *self.current_feed_kind.write() = FeedKind::Followed(with_replies); + *self.current_feed_kind.write() = FeedKind::List(list, with_replies); *self.thread_parent.write() = None; // Recompute as they switch @@ -301,11 +302,11 @@ impl Feed { let current_feed_kind = self.current_feed_kind.read().to_owned(); match current_feed_kind { - FeedKind::Followed(with_replies) => { - let mut followed_pubkeys: Vec = GLOBALS.people.get_followed_pubkeys(); + FeedKind::List(list, with_replies) => { + let mut pubkeys: Vec = GLOBALS.storage.get_people_in_list(list, None)?; if let Some(pubkey) = GLOBALS.signer.public_key() { - followed_pubkeys.push(pubkey); // add the user + pubkeys.push(pubkey); // add the user } let since = now - Duration::from_secs(GLOBALS.storage.read_setting_feed_chunk()); @@ -313,11 +314,11 @@ impl Feed { // FIXME we don't include delegated events. We should look for all events // delegated to people we follow and include those in the feed too. - let followed_events: Vec = GLOBALS + let events: Vec = GLOBALS .storage .find_events( &kinds_without_dms, - &followed_pubkeys, // pubkeys + &pubkeys, // pubkeys Some(since), |e| { e.created_at <= now // no future events @@ -336,7 +337,7 @@ impl Feed { .map(|e| e.id) .collect(); - *self.followed_feed.write() = followed_events; + *self.followed_feed.write() = events; } FeedKind::Inbox(indirect) => { if let Some(my_pubkey) = GLOBALS.signer.public_key() { diff --git a/gossip-lib/src/nip05.rs b/gossip-lib/src/nip05.rs index a97aa968..4ad1c4d3 100644 --- a/gossip-lib/src/nip05.rs +++ b/gossip-lib/src/nip05.rs @@ -91,7 +91,7 @@ pub async fn validate_nip05(person: Person) -> Result<(), Error> { Ok(()) } -pub async fn get_and_follow_nip05(nip05: String) -> Result<(), Error> { +pub async fn get_and_follow_nip05(nip05: String, public: bool) -> Result<(), Error> { // Split their DNS ID let (user, domain) = parse_nip05(&nip05)?; @@ -115,8 +115,8 @@ pub async fn get_and_follow_nip05(nip05: String) -> Result<(), Error> { ) .await?; - // Mark as followed - GLOBALS.people.follow(&pubkey, true)?; + // Mark as followed, publicly + GLOBALS.people.follow(&pubkey, true, public)?; tracing::info!("Followed {}", &nip05); diff --git a/gossip-lib/src/overlord/minion/mod.rs b/gossip-lib/src/overlord/minion/mod.rs index 79532b3e..f99b5213 100644 --- a/gossip-lib/src/overlord/minion/mod.rs +++ b/gossip-lib/src/overlord/minion/mod.rs @@ -16,8 +16,8 @@ use http::uri::{Parts, Scheme}; use http::Uri; use mime::Mime; use nostr_types::{ - ClientMessage, EventAddr, EventKind, Filter, Id, IdHex, IdHexPrefix, PublicKey, PublicKeyHex, - PublicKeyHexPrefix, RelayInformationDocument, RelayUrl, Unixtime, + ClientMessage, EventAddr, EventKind, Filter, Id, IdHex, PublicKey, PublicKeyHex, + RelayInformationDocument, RelayUrl, Unixtime, }; use reqwest::Response; use std::borrow::Cow; @@ -240,9 +240,6 @@ impl Minion { self.handle_overlord_message(message).await?; } - // Tell the overlord we are ready to receive commands - self.tell_overlord_we_are_ready().await?; - 'relayloop: loop { match self.loop_handler().await { Ok(_) => { @@ -431,11 +428,6 @@ impl Minion { Ok(()) } - async fn tell_overlord_we_are_ready(&self) -> Result<(), Error> { - self.to_overlord.send(ToOverlordMessage::MinionIsReady)?; - Ok(()) - } - async fn subscribe_augments(&mut self, job_id: u64, ids: Vec) -> Result<(), Error> { let mut event_kinds = crate::feed::feed_related_event_kinds(false); event_kinds.retain(|f| f.augments_feed_related()); @@ -492,10 +484,7 @@ impl Minion { let event_kinds = crate::feed::feed_related_event_kinds(true); if !followed_pubkeys.is_empty() { - let pkp: Vec = followed_pubkeys - .iter() - .map(|pk| Into::::into(*pk).prefix(16)) // quarter-size - .collect(); + let pkp: Vec = followed_pubkeys.iter().map(|pk| pk.into()).collect(); // feed related by people followed filters.push(Filter { @@ -511,11 +500,11 @@ impl Minion { // divine relays people write to (if using a client that does that). // BUT ONLY for people where this kind of data hasn't been received // in the last 8 hours (so we don't do it every client restart). - let keys_needing_relay_lists: Vec = GLOBALS + let keys_needing_relay_lists: Vec = GLOBALS .people - .get_followed_pubkeys_needing_relay_lists(&followed_pubkeys) + .get_subscribed_pubkeys_needing_relay_lists(&followed_pubkeys) .drain(..) - .map(|pk| Into::::into(pk).prefix(16)) // quarter-size + .map(|pk| pk.into()) .collect(); if !keys_needing_relay_lists.is_empty() { @@ -633,7 +622,7 @@ impl Minion { let filters: Vec = vec![ // Actual config stuff Filter { - authors: vec![pkh.clone().into()], + authors: vec![pkh.clone()], kinds: vec![ EventKind::Metadata, //EventKind::RecommendRelay, @@ -646,21 +635,21 @@ impl Minion { }, // GiftWraps to me, recent only Filter { - authors: vec![pkh.clone().into()], + authors: vec![pkh.clone()], kinds: vec![EventKind::GiftWrap], since: Some(giftwrap_since), ..Default::default() }, // Posts I wrote recently Filter { - authors: vec![pkh.into()], + authors: vec![pkh], kinds: crate::feed::feed_related_event_kinds(false), // not DMs since: Some(since), ..Default::default() }, ]; - self.subscribe(filters, "temp_config_feed", job_id).await?; + self.subscribe(filters, "config_feed", job_id).await?; } Ok(()) @@ -673,10 +662,7 @@ impl Minion { pubkeys: Vec, ) -> Result<(), Error> { if !pubkeys.is_empty() { - let pkp: Vec = pubkeys - .iter() - .map(|pk| Into::::into(*pk).prefix(16)) - .collect(); // quarter-size prefix + let pkp: Vec = pubkeys.iter().map(|pk| pk.into()).collect(); let filters: Vec = vec![Filter { authors: pkp, @@ -700,7 +686,7 @@ impl Minion { let event_kinds = crate::feed::feed_displayable_event_kinds(false); let filters: Vec = vec![Filter { - authors: vec![Into::::into(pubkey).prefix(16)], + authors: vec![pubkey.into()], kinds: event_kinds, // No since, just a limit on quantity of posts limit: Some(25), @@ -733,16 +719,9 @@ impl Minion { let mut filters: Vec = Vec::new(); if !vec_ids.is_empty() { - let idhp: Vec = vec_ids - .iter() - .map( - |id| id.prefix(16), // quarter-size - ) - .collect(); - // Get ancestors we know of so far filters.push(Filter { - ids: idhp, + ids: vec_ids.clone(), ..Default::default() }); @@ -783,13 +762,8 @@ impl Minion { // note: giftwraps can't be subscribed by channel. they are subscribed more // globally, and have to be limited to recent ones. - let mut authors: Vec = dmchannel - .keys() - .iter() - .map(Into::::into) - .map(|k| k.prefix(32)) - .collect(); - authors.push(pkh.prefix(32)); // add the user themselves + let mut authors: Vec = dmchannel.keys().iter().map(|k| k.into()).collect(); + authors.push(pkh); let filters: Vec = vec![Filter { authors, @@ -823,7 +797,7 @@ impl Minion { // create the filter let mut filter = Filter::new(); - filter.ids = ids.drain(..).map(|idhex| idhex.into()).collect(); + filter.ids = ids; tracing::trace!("{}: Event Filter: {} events", &self.url, filter.ids.len()); @@ -862,7 +836,7 @@ impl Minion { // build the filter let mut filter = Filter::new(); let pkh: PublicKeyHex = ea.author.into(); - filter.authors = vec![pkh.prefix(32)]; // half-size + filter.authors = vec![pkh]; filter.kinds = vec![ea.kind]; filter.d = vec![ea.d]; @@ -874,12 +848,7 @@ impl Minion { job_id: u64, mut pubkeys: Vec, ) -> Result<(), Error> { - let pkhp: Vec = pubkeys - .drain(..) - .map( - |pk| Into::::into(pk).prefix(16), // quarter-size - ) - .collect(); + let pkhp: Vec = pubkeys.drain(..).map(|pk| pk.into()).collect(); tracing::trace!("Temporarily subscribing to metadata on {}", &self.url); diff --git a/gossip-lib/src/overlord/mod.rs b/gossip-lib/src/overlord/mod.rs index 8b17bc67..2bc4a1bf 100644 --- a/gossip-lib/src/overlord/mod.rs +++ b/gossip-lib/src/overlord/mod.rs @@ -166,7 +166,7 @@ impl Overlord { // Separately subscribe to RelayList discovery for everyone we follow // We just do this once at startup. Relay lists don't change that frequently. - let followed = GLOBALS.people.get_followed_pubkeys(); + let followed = GLOBALS.people.get_subscribed_pubkeys(); self.subscribe_discover(followed, None).await?; // Separately subscribe to our outbox events on our write relays @@ -561,14 +561,14 @@ impl Overlord { ToOverlordMessage::FetchEventAddr(ea) => { self.fetch_event_addr(ea).await?; } - ToOverlordMessage::FollowPubkey(pubkey) => { - self.follow_pubkey(pubkey).await?; + ToOverlordMessage::FollowPubkey(pubkey, public) => { + self.follow_pubkey(pubkey, public).await?; } - ToOverlordMessage::FollowNip05(nip05) => { - Self::follow_nip05(nip05).await?; + ToOverlordMessage::FollowNip05(nip05, public) => { + Self::follow_nip05(nip05, public).await?; } - ToOverlordMessage::FollowNprofile(nprofile) => { - self.follow_nprofile(nprofile).await?; + ToOverlordMessage::FollowNprofile(nprofile, public) => { + self.follow_nprofile(nprofile, public).await?; } ToOverlordMessage::GeneratePrivateKey(password) => { Self::generate_private_key(password).await?; @@ -585,10 +585,6 @@ impl Overlord { ToOverlordMessage::Like(id, pubkey) => { self.like(id, pubkey).await?; } - ToOverlordMessage::MinionIsReady => { - // internal - // currently ignored - } ToOverlordMessage::MinionJobComplete(url, job_id) => { self.finish_job(url, Some(job_id), None)?; } @@ -642,8 +638,8 @@ impl Overlord { ToOverlordMessage::ReengageMinion(url, persistent_jobs) => { self.engage_minion(url, persistent_jobs).await?; } - ToOverlordMessage::RefreshFollowedMetadata => { - self.refresh_followed_metadata().await?; + ToOverlordMessage::RefreshSubscribedMetadata => { + self.refresh_subscribed_metadata().await?; } ToOverlordMessage::Repost(id) => { self.repost(id).await?; @@ -1002,17 +998,17 @@ impl Overlord { } /// Follow a person by `PublicKey` - pub async fn follow_pubkey(&mut self, pubkey: PublicKey) -> Result<(), Error> { - GLOBALS.people.follow(&pubkey, true)?; + pub async fn follow_pubkey(&mut self, pubkey: PublicKey, public: bool) -> Result<(), Error> { + GLOBALS.people.follow(&pubkey, true, public)?; self.subscribe_discover(vec![pubkey], None).await?; tracing::debug!("Followed {}", &pubkey.as_hex_string()); Ok(()) } /// Follow a person by a nip-05 address - pub async fn follow_nip05(nip05: String) -> Result<(), Error> { + pub async fn follow_nip05(nip05: String, public: bool) -> Result<(), Error> { std::mem::drop(tokio::spawn(async move { - if let Err(e) = crate::nip05::get_and_follow_nip05(nip05).await { + if let Err(e) = crate::nip05::get_and_follow_nip05(nip05, public).await { tracing::error!("{}", e); } })); @@ -1020,8 +1016,8 @@ impl Overlord { } /// Follow a person by a `Profile` (nprofile1...) - pub async fn follow_nprofile(&mut self, nprofile: Profile) -> Result<(), Error> { - GLOBALS.people.follow(&nprofile.pubkey, true)?; + pub async fn follow_nprofile(&mut self, nprofile: Profile, public: bool) -> Result<(), Error> { + GLOBALS.people.follow(&nprofile.pubkey, true, public)?; // Set their relays for relay in nprofile.relays.iter() { @@ -1692,8 +1688,8 @@ impl Overlord { /// Refresh metadata for everybody who is followed /// This gets it whether we had it or not. Because it might have changed. - pub async fn refresh_followed_metadata(&mut self) -> Result<(), Error> { - let mut pubkeys = GLOBALS.people.get_followed_pubkeys(); + pub async fn refresh_subscribed_metadata(&mut self) -> Result<(), Error> { + let mut pubkeys = GLOBALS.people.get_subscribed_pubkeys(); // add own pubkey as well if let Some(pubkey) = GLOBALS.signer.public_key() { @@ -2315,8 +2311,8 @@ impl Overlord { txn.commit()?; - // Follow all those pubkeys, and unfollow everbody else if merge=false - GLOBALS.people.follow_all(&pubkeys, merge)?; + // Follow all those pubkeys publicly, and unfollow everbody else if merge=false + GLOBALS.people.follow_all(&pubkeys, merge, true)?; // Update last_contact_list_edit let last_edit = if merge { @@ -2432,8 +2428,8 @@ impl Overlord { } } - // Mute all those pubkeys, and unmute everbody else if merge=false - GLOBALS.people.mute_all(&pubkeys, merge)?; + // Mute all those pubkeys publicly, and unmute everbody else if merge=false + GLOBALS.people.mute_all(&pubkeys, merge, true)?; // Update last_must_list_edit let last_edit = if merge { diff --git a/gossip-lib/src/people.rs b/gossip-lib/src/people.rs index 16fd69af..5857cf06 100644 --- a/gossip-lib/src/people.rs +++ b/gossip-lib/src/people.rs @@ -155,10 +155,28 @@ impl People { }); } - /// Get all the pubkeys that the user follows + /// Get all the pubkeys that the user subscribes to in any list + pub fn get_subscribed_pubkeys(&self) -> Vec { + // We subscribe to all people in all lists. + // This is no longer synonomous with the ContactList list + match GLOBALS.storage.get_people_in_all_followed_lists() { + Ok(people) => people, + Err(e) => { + tracing::error!("{}", e); + vec![] + } + } + } + + /// Get all the pubkeys in the Followed list pub fn get_followed_pubkeys(&self) -> Vec { - match GLOBALS.storage.get_people_in_list(PersonList::Followed) { - Ok(list) => list, + // We subscribe to all people in all lists. + // This is no longer synonomous with the ContactList list + match GLOBALS + .storage + .get_people_in_list(PersonList::Followed, None) + { + Ok(people) => people, Err(e) => { tracing::error!("{}", e); vec![] @@ -168,8 +186,8 @@ impl People { /// Get all the pubkeys that the user mutes pub fn get_muted_pubkeys(&self) -> Vec { - match GLOBALS.storage.get_people_in_list(PersonList::Muted) { - Ok(list) => list, + match GLOBALS.storage.get_people_in_list(PersonList::Muted, None) { + Ok(people) => people, Err(e) => { tracing::error!("{}", e); vec![] @@ -179,11 +197,25 @@ impl People { /// Is the given pubkey followed? pub fn is_followed(&self, pubkey: &PublicKey) -> bool { - self.get_followed_pubkeys().contains(pubkey) + match GLOBALS + .storage + .is_person_in_list(pubkey, PersonList::Followed) + { + Ok(answer) => answer, + _ => false, + } + } + + /// Is the given pubkey muted? + pub fn is_muted(&self, pubkey: &PublicKey) -> bool { + match GLOBALS.storage.is_person_in_list(pubkey, PersonList::Muted) { + Ok(answer) => answer, + _ => false, + } } /// Get all the pubkeys that need relay lists (from the given set) - pub fn get_followed_pubkeys_needing_relay_lists( + pub fn get_subscribed_pubkeys_needing_relay_lists( &self, among_these: &[PublicKey], ) -> Vec { @@ -670,13 +702,16 @@ impl People { } /// Follow (or unfollow) the public key - pub fn follow(&self, pubkey: &PublicKey, follow: bool) -> Result<(), Error> { + pub fn follow(&self, pubkey: &PublicKey, follow: bool, public: bool) -> Result<(), Error> { let mut txn = GLOBALS.storage.get_write_txn()?; if follow { - GLOBALS - .storage - .add_person_to_list(pubkey, PersonList::Followed, Some(&mut txn))?; + GLOBALS.storage.add_person_to_list( + pubkey, + PersonList::Followed, + public, + Some(&mut txn), + )?; } else { GLOBALS.storage.remove_person_from_list( pubkey, @@ -697,7 +732,12 @@ impl People { /// Follow all these public keys. /// This does not publish any events. - pub(crate) fn follow_all(&self, pubkeys: &[PublicKey], merge: bool) -> Result<(), Error> { + pub(crate) fn follow_all( + &self, + pubkeys: &[PublicKey], + public: bool, + merge: bool, + ) -> Result<(), Error> { let mut txn = GLOBALS.storage.get_write_txn()?; if !merge { @@ -707,9 +747,12 @@ impl People { } for pubkey in pubkeys { - GLOBALS - .storage - .add_person_to_list(pubkey, PersonList::Followed, Some(&mut txn))?; + GLOBALS.storage.add_person_to_list( + pubkey, + PersonList::Followed, + public, + Some(&mut txn), + )?; GLOBALS.ui_people_to_invalidate.write().push(*pubkey); } @@ -766,7 +809,7 @@ impl People { } /// Mute (or unmute) a public key - pub fn mute(&self, pubkey: &PublicKey, mute: bool) -> Result<(), Error> { + pub fn mute(&self, pubkey: &PublicKey, mute: bool, public: bool) -> Result<(), Error> { let mut txn = GLOBALS.storage.get_write_txn()?; if mute { @@ -776,9 +819,12 @@ impl People { } } - GLOBALS - .storage - .add_person_to_list(pubkey, PersonList::Muted, Some(&mut txn))?; + GLOBALS.storage.add_person_to_list( + pubkey, + PersonList::Muted, + public, + Some(&mut txn), + )?; } else { GLOBALS .storage @@ -796,7 +842,12 @@ impl People { Ok(()) } - pub(crate) fn mute_all(&self, pubkeys: &[PublicKey], merge: bool) -> Result<(), Error> { + pub(crate) fn mute_all( + &self, + pubkeys: &[PublicKey], + merge: bool, + public: bool, + ) -> Result<(), Error> { let mut txn = GLOBALS.storage.get_write_txn()?; if !merge { @@ -806,9 +857,12 @@ impl People { } for pubkey in pubkeys { - GLOBALS - .storage - .add_person_to_list(pubkey, PersonList::Muted, Some(&mut txn))?; + GLOBALS.storage.add_person_to_list( + pubkey, + PersonList::Muted, + public, + Some(&mut txn), + )?; GLOBALS.ui_people_to_invalidate.write().push(*pubkey); } diff --git a/gossip-lib/src/process.rs b/gossip-lib/src/process.rs index d8be22ed..1f4f0278 100644 --- a/gossip-lib/src/process.rs +++ b/gossip-lib/src/process.rs @@ -75,7 +75,8 @@ pub async fn process_new_event( return Ok(()); } EventFilterAction::MuteAuthor => { - GLOBALS.people.mute(&event.pubkey, true)?; + let public = true; + GLOBALS.people.mute(&event.pubkey, true, public)?; return Ok(()); } } diff --git a/gossip-lib/src/relay_picker_hooks.rs b/gossip-lib/src/relay_picker_hooks.rs index 2a891e95..2b75da2d 100644 --- a/gossip-lib/src/relay_picker_hooks.rs +++ b/gossip-lib/src/relay_picker_hooks.rs @@ -46,10 +46,10 @@ impl RelayPickerHooks for Hooks { } /// Returns the public keys of all the people followed + // this API name has become difficult.. fn get_followed_pubkeys(&self) -> Vec { - // CHANGE TO GET THIS FROM THE CURRENT FEED - - GLOBALS.people.get_followed_pubkeys() + // ..We actually want all the people subscribed, which is a bigger list + GLOBALS.people.get_subscribed_pubkeys() } /// Adjusts the score for a given relay, perhaps based on relay-specific metrics diff --git a/gossip-lib/src/settings.rs b/gossip-lib/src/settings.rs index 264f68a9..b8e3fcb1 100644 --- a/gossip-lib/src/settings.rs +++ b/gossip-lib/src/settings.rs @@ -57,6 +57,7 @@ pub struct Settings { pub replies_chunk: u64, pub person_feed_chunk: u64, pub overlap: u64, + pub custom_person_list_names: [String; 10], // Event Selection pub reposts: bool, @@ -141,6 +142,7 @@ impl Default for Settings { replies_chunk: default_setting!(replies_chunk), person_feed_chunk: default_setting!(person_feed_chunk), overlap: default_setting!(overlap), + custom_person_list_names: default_setting!(custom_person_list_names), reposts: default_setting!(reposts), show_long_form: default_setting!(show_long_form), show_mentions: default_setting!(show_mentions), @@ -221,6 +223,7 @@ impl Settings { replies_chunk: load_setting!(replies_chunk), person_feed_chunk: load_setting!(person_feed_chunk), overlap: load_setting!(overlap), + custom_person_list_names: load_setting!(custom_person_list_names), reposts: load_setting!(reposts), show_long_form: load_setting!(show_long_form), show_mentions: load_setting!(show_mentions), @@ -297,6 +300,7 @@ impl Settings { save_setting!(replies_chunk, self, txn); save_setting!(person_feed_chunk, self, txn); save_setting!(overlap, self, txn); + save_setting!(custom_person_list_names, self, txn); save_setting!(reposts, self, txn); save_setting!(show_long_form, self, txn); save_setting!(show_mentions, self, txn); diff --git a/gossip-lib/src/storage/migrations/mod.rs b/gossip-lib/src/storage/migrations/mod.rs index 08de5bf0..f104a1d8 100644 --- a/gossip-lib/src/storage/migrations/mod.rs +++ b/gossip-lib/src/storage/migrations/mod.rs @@ -1,14 +1,16 @@ -use super::types::{Person2, PersonRelay1, Settings1, Settings2, Theme1, ThemeVariant1}; +use super::types::{ + Person2, PersonList1, PersonRelay1, Settings1, Settings2, Theme1, ThemeVariant1, +}; use super::Storage; use crate::error::{Error, ErrorKind}; -use crate::people::PersonList; use heed::types::UnalignedSlice; use heed::{DatabaseFlags, RwTxn}; -use nostr_types::{Event, Id, RelayUrl, Signature}; +use nostr_types::{Event, Id, PublicKey, RelayUrl, Signature}; use speedy::{Readable, Writable}; +use std::collections::HashMap; impl Storage { - const MAX_MIGRATION_LEVEL: u32 = 12; + const MAX_MIGRATION_LEVEL: u32 = 13; pub(super) fn migrate(&self, mut level: u32) -> Result<(), Error> { if level > Self::MAX_MIGRATION_LEVEL { @@ -64,6 +66,10 @@ impl Storage { let _ = self.db_events1()?; let _ = self.db_event_tag_index1()?; } + 12 => { + let _ = self.db_person_lists1()?; + let _ = self.db_person_lists2()?; + } _ => {} }; Ok(()) @@ -123,6 +129,10 @@ impl Storage { tracing::info!("{prefix}: removing now unused event_references_person index..."); self.remove_event_references_person(txn)?; } + 12 => { + tracing::info!("{prefix}: migrating lists..."); + self.migrate_lists(txn)?; + } _ => panic!("Unreachable migration level"), }; @@ -404,13 +414,13 @@ impl Storage { let mut count: usize = 0; let mut followed_count: usize = 0; for person1 in self.filter_people1(|_| true)?.iter() { - let mut lists: Vec = Vec::new(); + let mut lists: Vec = Vec::new(); if person1.followed { - lists.push(PersonList::Followed); + lists.push(PersonList1::Followed); followed_count += 1; } if person1.muted { - lists.push(PersonList::Muted); + lists.push(PersonList1::Muted); } if !lists.is_empty() { self.write_person_lists1(&person1.pubkey, lists, Some(txn))?; @@ -557,4 +567,28 @@ impl Storage { Ok(()) } + + pub fn migrate_lists<'a>(&'a self, txn: &mut RwTxn<'a>) -> Result<(), Error> { + let loop_txn = self.env.read_txn()?; + for result in self.db_person_lists1()?.iter(&loop_txn)? { + let (key, val) = result?; + let pubkey = PublicKey::from_bytes(key, true)?; + let mut person_lists = val + .iter() + .map(|u| (*u).into()) + .collect::>(); + let new_person_lists: HashMap = + person_lists.drain(..).map(|l| (l, true)).collect(); + + self.write_person_lists2(&pubkey, new_person_lists, Some(txn))?; + } + + // remove db_person_lists1 + { + self.db_person_lists1()?.clear(txn)?; + // heed doesn't expose mdb_drop(1) yet, so we can't actually remove this database. + } + + Ok(()) + } } diff --git a/gossip-lib/src/storage/mod.rs b/gossip-lib/src/storage/mod.rs index b635258e..11c496c7 100644 --- a/gossip-lib/src/storage/mod.rs +++ b/gossip-lib/src/storage/mod.rs @@ -27,6 +27,7 @@ mod hashtags1; mod people1; mod people2; mod person_lists1; +mod person_lists2; mod person_relays1; mod relationships1; mod relays1; @@ -95,7 +96,7 @@ macro_rules! def_setting { match self.general.get(&txn, $string) { Err(_) => $default, Ok(None) => $default, - Ok(Some(bytes)) => match $type::read_from_buffer(bytes) { + Ok(Some(bytes)) => match <$type>::read_from_buffer(bytes) { Ok(val) => val, Err(_) => $default, } @@ -134,7 +135,7 @@ impl Storage { pub(crate) fn new() -> Result { let mut builder = EnvOpenOptions::new(); unsafe { - builder.flags(EnvFlags::NO_SYNC | EnvFlags::NO_TLS); + builder.flags(EnvFlags::NO_TLS); } // builder.max_readers(126); // this is the default builder.max_dbs(32); @@ -284,7 +285,7 @@ impl Storage { #[inline] pub(crate) fn db_person_lists(&self) -> Result { - self.db_person_lists1() + self.db_person_lists2() } // Database length functions --------------------------------- @@ -722,6 +723,23 @@ impl Storage { 60 * 60 * 24 * 30 ); def_setting!(overlap, b"overlap", u64, 300); + def_setting!( + custom_person_list_names, + b"custom_person_list_names", + [String; 10], + [ + "Priority".to_owned(), + "Custom List 2".to_owned(), + "Custom List 3".to_owned(), + "Custom List 4".to_owned(), + "Custom List 5".to_owned(), + "Custom List 6".to_owned(), + "Custom List 7".to_owned(), + "Custom List 8".to_owned(), + "Custom List 9".to_owned(), + "Custom List 10".to_owned(), + ] + ); def_setting!(reposts, b"reposts", bool, true); def_setting!(show_long_form, b"show_long_form", bool, false); def_setting!(show_mentions, b"show_mentions", bool, true); @@ -1777,10 +1795,19 @@ impl Storage { // Whether or not the Gossip user already reacted to this event let mut self_already_reacted = false; + // Get the event (once self-reactions get deleted we can remove this) + let maybe_target_event = self.read_event(id)?; + // Collect up to one reaction per pubkey let mut phase1: HashMap = HashMap::new(); for (_, rel) in self.find_relationships(id)? { if let Relationship::Reaction(pubkey, reaction) = rel { + if let Some(target_event) = &maybe_target_event { + if target_event.pubkey == pubkey { + // Do not let people like their own post + continue; + } + } let symbol: char = if let Some(ch) = reaction.chars().next() { ch } else { @@ -1820,9 +1847,19 @@ impl Storage { /// Get whether an event was deleted, and if so the optional reason pub fn get_deletion(&self, id: Id) -> Result, Error> { - for (_, rel) in self.find_relationships(id)? { + for (target_id, rel) in self.find_relationships(id)? { if let Relationship::Deletion(deletion) = rel { - return Ok(Some(deletion)); + if let Some(delete_event) = self.read_event(id)? { + if let Some(target_event) = self.read_event(target_id)? { + // Only if the authors match + if target_event.pubkey == delete_event.pubkey { + return Ok(Some(deletion)); + } + } else { + // presume the authors will match for now + return Ok(Some(deletion)); + } + } } } Ok(None) @@ -1844,30 +1881,58 @@ impl Storage { } // reacts to - if let Some((id, reaction, _maybe_url)) = event.reacts_to() { - self.write_relationship( - id, - event.id, - Relationship::Reaction(event.pubkey, reaction), - Some(txn), - )?; - - invalidate.push(id); + if let Some((reacted_to_id, reaction, _maybe_url)) = event.reacts_to() { + if let Some(reacted_to_event) = self.read_event(reacted_to_id)? { + // Only if they are different people (no liking your own posts) + if reacted_to_event.pubkey != event.pubkey { + self.write_relationship( + reacted_to_id, // event reacted to + event.id, // the reaction event id + Relationship::Reaction(event.pubkey, reaction), + Some(txn), + )?; + } + invalidate.push(reacted_to_id); + } else { + // Store the reaction to the event we dont have yet. + // We filter bad ones when reading them back too, so even if this + // turns out to be a reaction by the author, they can't like + // their own post + self.write_relationship( + reacted_to_id, // event reacted to + event.id, // the reaction event id + Relationship::Reaction(event.pubkey, reaction), + Some(txn), + )?; + invalidate.push(reacted_to_id); + } } // deletes - if let Some((ids, reason)) = event.deletes() { - invalidate.extend(&ids); - - for id in ids { + if let Some((deleted_event_ids, reason)) = event.deletes() { + invalidate.extend(&deleted_event_ids); + for deleted_event_id in deleted_event_ids { // since it is a delete, we don't actually desire the event. - - self.write_relationship( - id, - event.id, - Relationship::Deletion(reason.clone()), - Some(txn), - )?; + if let Some(deleted_event) = self.read_event(deleted_event_id)? { + // Only if it is the same author + if deleted_event.pubkey == event.pubkey { + self.write_relationship( + deleted_event_id, + event.id, + Relationship::Deletion(reason.clone()), + Some(txn), + )?; + } + } else { + // We don't have the deleted event. Presume it is okay. We check again + // when we read these back + self.write_relationship( + deleted_event_id, + event.id, + Relationship::Deletion(reason.clone()), + Some(txn), + )?; + } } } @@ -2090,7 +2155,12 @@ impl Storage { let mut map: HashMap = HashMap::new(); for event in &events { - let unread = 1 - self.is_event_viewed(event.id)? as usize; + let unread: usize = if event.pubkey == my_pubkey { + // Do not count self-authored events as unread, irrespective of whether they are viewed + 0 + } else { + 1 - self.is_event_viewed(event.id)? as usize + }; if event.kind == EventKind::EncryptedDirectMessage { let time = event.created_at; let dmchannel = match DmChannel::from_event(event, Some(my_pubkey)) { @@ -2260,23 +2330,34 @@ impl Storage { } /// Read person lists - pub fn read_person_lists(&self, pubkey: &PublicKey) -> Result, Error> { - self.read_person_lists1(pubkey) + pub fn read_person_lists( + &self, + pubkey: &PublicKey, + ) -> Result, Error> { + self.read_person_lists2(pubkey) } /// Write person lists pub fn write_person_lists<'a>( &'a self, pubkey: &PublicKey, - lists: Vec, + lists: HashMap, rw_txn: Option<&mut RwTxn<'a>>, ) -> Result<(), Error> { - self.write_person_lists1(pubkey, lists, rw_txn) + self.write_person_lists2(pubkey, lists, rw_txn) } /// Get people in a person list - pub fn get_people_in_list(&self, list: PersonList) -> Result, Error> { - self.get_people_in_list1(list) + pub fn get_people_in_list( + &self, + list: PersonList, + public: Option, + ) -> Result, Error> { + self.get_people_in_list2(list, public) + } + + pub fn get_people_in_all_followed_lists(&self) -> Result, Error> { + self.get_people_in_all_followed_lists2() } /// Empty a person list @@ -2286,13 +2367,19 @@ impl Storage { list: PersonList, rw_txn: Option<&mut RwTxn<'a>>, ) -> Result<(), Error> { - self.clear_person_list1(list, rw_txn) + self.clear_person_list2(list, rw_txn) } /// Is a person in a list? pub fn is_person_in_list(&self, pubkey: &PublicKey, list: PersonList) -> Result { - let lists = self.read_person_lists(pubkey)?; - Ok(lists.contains(&list)) + let map = self.read_person_lists(pubkey)?; + Ok(map.contains_key(&list)) + } + + /// Is the person in any list we subscribe to? + pub fn is_person_subscribed_to(&self, pubkey: &PublicKey) -> Result { + let map = self.read_person_lists(pubkey)?; + Ok(map.iter().any(|l| l.0.subscribe())) } /// Add a person to a list @@ -2300,13 +2387,12 @@ impl Storage { &'a self, pubkey: &PublicKey, list: PersonList, + public: bool, rw_txn: Option<&mut RwTxn<'a>>, ) -> Result<(), Error> { - let mut lists = self.read_person_lists(pubkey)?; - if !lists.iter().any(|s| *s == list) { - lists.push(list.to_owned()); - } - self.write_person_lists(pubkey, lists, rw_txn) + let mut map = self.read_person_lists(pubkey)?; + map.insert(list, public); + self.write_person_lists(pubkey, map, rw_txn) } /// Remove a person from a list @@ -2316,8 +2402,8 @@ impl Storage { list: PersonList, rw_txn: Option<&mut RwTxn<'a>>, ) -> Result<(), Error> { - let mut lists = self.read_person_lists(pubkey)?; - let lists: Vec = lists.drain(..).filter(|s| *s != list).collect(); - self.write_person_lists(pubkey, lists, rw_txn) + let mut map = self.read_person_lists(pubkey)?; + map.remove(&list); + self.write_person_lists(pubkey, map, rw_txn) } } diff --git a/gossip-lib/src/storage/person_lists1.rs b/gossip-lib/src/storage/person_lists1.rs index bcf802c4..99b3ef1d 100644 --- a/gossip-lib/src/storage/person_lists1.rs +++ b/gossip-lib/src/storage/person_lists1.rs @@ -43,15 +43,6 @@ impl Storage { } } - pub(crate) fn read_person_lists1(&self, pubkey: &PublicKey) -> Result, Error> { - let key: Vec = pubkey.to_bytes(); - let txn = self.env.read_txn()?; - Ok(match self.db_person_lists1()?.get(&txn, &key)? { - Some(bytes) => bytes.iter().map(|u| (*u).into()).collect(), - None => vec![], - }) - } - pub(crate) fn write_person_lists1<'a>( &'a self, pubkey: &PublicKey, @@ -77,62 +68,4 @@ impl Storage { Ok(()) } - - pub(crate) fn get_people_in_list1(&self, list: PersonList) -> Result, Error> { - let txn = self.env.read_txn()?; - let mut pubkeys: Vec = Vec::new(); - for result in self.db_person_lists1()?.iter(&txn)? { - let (key, val) = result?; - let pubkey = PublicKey::from_bytes(key, true)?; - let person_lists = val.iter().map(|u| (*u).into()).collect::>(); - if person_lists.iter().any(|s| *s == list) { - pubkeys.push(pubkey); - } - } - Ok(pubkeys) - } - - pub(crate) fn clear_person_list1<'a>( - &'a self, - list: PersonList, - rw_txn: Option<&mut RwTxn<'a>>, - ) -> Result<(), Error> { - let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> { - let mut fixed: Vec<(PublicKey, Vec)> = Vec::new(); - - // Collect records that require changing - for result in self.db_person_lists1()?.iter(txn)? { - let (key, val) = result?; - let pubkey = PublicKey::from_bytes(key, true)?; - let mut person_lists = val.iter().map(|u| (*u).into()).collect::>(); - if person_lists.contains(&list) { - person_lists = person_lists.drain(..).filter(|l| *l != list).collect(); - let bytes = person_lists - .iter() - .map(|l| (*l).into()) - .collect::>(); - fixed.push((pubkey, bytes)); - } - } - - // Change them - for (pubkey, bytes) in fixed.drain(..) { - let key: Vec = pubkey.to_bytes(); - self.db_person_lists1()?.put(txn, &key, &bytes)?; - } - - Ok(()) - }; - - match rw_txn { - Some(txn) => f(txn)?, - None => { - let mut txn = self.env.write_txn()?; - f(&mut txn)?; - txn.commit()?; - } - }; - - Ok(()) - } } diff --git a/gossip-lib/src/storage/person_lists2.rs b/gossip-lib/src/storage/person_lists2.rs new file mode 100644 index 00000000..ddc84152 --- /dev/null +++ b/gossip-lib/src/storage/person_lists2.rs @@ -0,0 +1,172 @@ +use super::types::PersonList1; +use crate::error::Error; +use crate::storage::{RawDatabase, Storage}; +use heed::types::UnalignedSlice; +use heed::RwTxn; +use nostr_types::PublicKey; +use speedy::{Readable, Writable}; +use std::collections::HashMap; +use std::sync::Mutex; + +// Pubkey -> Vec<(PersonList, bool)> // bool is if private or not +// key: pubkey.as_bytes() + +static PERSON_LISTS2_DB_CREATE_LOCK: Mutex<()> = Mutex::new(()); +static mut PERSON_LISTS2_DB: Option = None; + +impl Storage { + pub(super) fn db_person_lists2(&self) -> Result { + unsafe { + if let Some(db) = PERSON_LISTS2_DB { + Ok(db) + } else { + // Lock. This drops when anything returns. + let _lock = PERSON_LISTS2_DB_CREATE_LOCK.lock(); + + // In case of a race, check again + if let Some(db) = PERSON_LISTS2_DB { + return Ok(db); + } + + // Create it. We know that nobody else is doing this and that + // it cannot happen twice. + let mut txn = self.env.write_txn()?; + let db = self + .env + .database_options() + .types::, UnalignedSlice>() + // no .flags needed + .name("person_lists_2") + .create(&mut txn)?; + txn.commit()?; + PERSON_LISTS2_DB = Some(db); + Ok(db) + } + } + } + + pub(crate) fn read_person_lists2( + &self, + pubkey: &PublicKey, + ) -> Result, Error> { + let key: Vec = pubkey.to_bytes(); + let txn = self.env.read_txn()?; + Ok(match self.db_person_lists2()?.get(&txn, &key)? { + None => HashMap::new(), + Some(bytes) => HashMap::::read_from_buffer(bytes)?, + }) + } + + pub(crate) fn write_person_lists2<'a>( + &'a self, + pubkey: &PublicKey, + map: HashMap, + rw_txn: Option<&mut RwTxn<'a>>, + ) -> Result<(), Error> { + let key: Vec = pubkey.to_bytes(); + let bytes: Vec = map.write_to_vec()?; + + let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> { + self.db_person_lists2()?.put(txn, &key, &bytes)?; + Ok(()) + }; + + match rw_txn { + Some(txn) => f(txn)?, + None => { + let mut txn = self.env.write_txn()?; + f(&mut txn)?; + txn.commit()?; + } + }; + + Ok(()) + } + + pub(crate) fn get_people_in_all_followed_lists2(&self) -> Result, Error> { + let txn = self.env.read_txn()?; + let mut pubkeys: Vec = Vec::new(); + for result in self.db_person_lists2()?.iter(&txn)? { + let (key, val) = result?; + let pubkey = PublicKey::from_bytes(key, true)?; + let map = HashMap::::read_from_buffer(val)?; + if map.keys().any(|list| list.subscribe()) { + pubkeys.push(pubkey); + } + } + Ok(pubkeys) + } + + pub(crate) fn get_people_in_list2( + &self, + list: PersonList1, + public: Option, + ) -> Result, Error> { + let txn = self.env.read_txn()?; + let mut pubkeys: Vec = Vec::new(); + for result in self.db_person_lists2()?.iter(&txn)? { + let (key, val) = result?; + let pubkey = PublicKey::from_bytes(key, true)?; + let map = HashMap::::read_from_buffer(val)?; + if let Some(p) = map.get(&list) { + match public { + Some(p2) => { + if p2 == *p { + pubkeys.push(pubkey); + } + } + None => { + pubkeys.push(pubkey); + } + } + } + } + Ok(pubkeys) + } + + pub(crate) fn clear_person_list2<'a>( + &'a self, + list: PersonList1, + rw_txn: Option<&mut RwTxn<'a>>, + ) -> Result<(), Error> { + let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> { + let mut fixed: Vec<(PublicKey, HashMap)> = Vec::new(); + + // Collect records that require changing + // (lmdb doesn't like changing them in place while iterating) + for result in self.db_person_lists2()?.iter(txn)? { + let (key, val) = result?; + let pubkey = PublicKey::from_bytes(key, true)?; + let mut map = HashMap::::read_from_buffer(val)?; + if map.contains_key(&list) { + map.remove(&list); + fixed.push((pubkey, map)); + } + } + + // Change them + for (pubkey, map) in fixed.drain(..) { + let key: Vec = pubkey.to_bytes(); + if map.is_empty() { + self.db_person_lists2()?.delete(txn, &key)?; + } else { + let bytes: Vec = map.write_to_vec()?; + self.db_person_lists2()?.put(txn, &key, &bytes)?; + } + } + + Ok(()) + }; + + match rw_txn { + Some(txn) => f(txn)?, + None => { + let mut txn = self.env.write_txn()?; + f(&mut txn)?; + txn.commit()?; + } + }; + + Ok(()) + } +} diff --git a/gossip-lib/src/storage/types/person_list1.rs b/gossip-lib/src/storage/types/person_list1.rs index 848156f4..dd103042 100644 --- a/gossip-lib/src/storage/types/person_list1.rs +++ b/gossip-lib/src/storage/types/person_list1.rs @@ -1,10 +1,14 @@ +use crate::globals::GLOBALS; +use speedy::{Readable, Writable}; + /// Lists people can be added to -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Readable, Writable)] #[repr(u8)] pub enum PersonList1 { Muted = 0, Followed = 1, - Priority = 2, + + // custom starts at 10 to leave space Custom(u8), } @@ -13,7 +17,6 @@ impl From for PersonList1 { match u { 0 => PersonList1::Muted, 1 => PersonList1::Followed, - 2 => PersonList1::Priority, u => PersonList1::Custom(u), } } @@ -24,8 +27,42 @@ impl From for u8 { match e { PersonList1::Muted => 0, PersonList1::Followed => 1, - PersonList1::Priority => 2, PersonList1::Custom(u) => u, } } } + +impl PersonList1 { + /// Custom lists (from 0-9, humans number them from 1-10) + pub fn custom(index: u8) -> Option { + if index > 9 { + None + } else { + Some(PersonList1::Custom(index + 10)) + } + } + + pub fn name(&self) -> String { + match *self { + PersonList1::Muted => "Muted".to_string(), + PersonList1::Followed => "Followed".to_string(), + PersonList1::Custom(u) => { + if (10..=19).contains(&u) { + GLOBALS.storage.read_setting_custom_person_list_names()[u as usize - 10].clone() + } else if u > 19 { + format!("Custom List {}", u - 9) // humans count from 1 + } else { + format!("Undefined list in slot={}", u) + } + } + } + } + + pub fn subscribe(&self) -> bool { + match *self { + PersonList1::Muted => false, + PersonList1::Followed => true, + PersonList1::Custom(_) => true, + } + } +}