feat: screening (#96)
* . * . * refactor * . * screening * add report user function * add danger and warning styles * update deps * update * fix line height * .
This commit is contained in:
177
Cargo.lock
generated
177
Cargo.lock
generated
@@ -163,7 +163,7 @@ dependencies = [
|
|||||||
"enumflags2",
|
"enumflags2",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"url",
|
"url",
|
||||||
@@ -224,9 +224,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-fs"
|
name = "async-fs"
|
||||||
version = "2.1.2"
|
version = "2.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
|
checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"blocking",
|
"blocking",
|
||||||
@@ -235,9 +235,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-io"
|
name = "async-io"
|
||||||
version = "2.4.1"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3"
|
checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -248,8 +248,7 @@ dependencies = [
|
|||||||
"polling",
|
"polling",
|
||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"slab",
|
"slab",
|
||||||
"tracing",
|
"windows-sys 0.60.2",
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -276,9 +275,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-process"
|
name = "async-process"
|
||||||
version = "2.3.1"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc"
|
checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-io",
|
"async-io",
|
||||||
@@ -290,7 +289,6 @@ dependencies = [
|
|||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-lite 2.6.0",
|
"futures-lite 2.6.0",
|
||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"tracing",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -306,9 +304,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-signal"
|
name = "async-signal"
|
||||||
version = "0.2.11"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d"
|
checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-io",
|
"async-io",
|
||||||
"async-lock",
|
"async-lock",
|
||||||
@@ -319,7 +317,7 @@ dependencies = [
|
|||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"slab",
|
"slab",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -449,9 +447,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-lc-rs"
|
name = "aws-lc-rs"
|
||||||
version = "1.13.2"
|
version = "1.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08b5d4e069cbc868041a64bd68dc8cb39a0d79585cd6c5a24caa8c2d622121be"
|
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-sys",
|
"aws-lc-sys",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -644,7 +642,7 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "blade-graphics"
|
name = "blade-graphics"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/kvark/blade?rev=416375211bb0b5826b3584dccdb6a43369e499ad#416375211bb0b5826b3584dccdb6a43369e499ad"
|
source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ash",
|
"ash",
|
||||||
"ash-window",
|
"ash-window",
|
||||||
@@ -677,7 +675,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "blade-macros"
|
name = "blade-macros"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/kvark/blade?rev=416375211bb0b5826b3584dccdb6a43369e499ad#416375211bb0b5826b3584dccdb6a43369e499ad"
|
source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -687,7 +685,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "blade-util"
|
name = "blade-util"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/kvark/blade?rev=416375211bb0b5826b3584dccdb6a43369e499ad#416375211bb0b5826b3584dccdb6a43369e499ad"
|
source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blade-graphics",
|
"blade-graphics",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -774,9 +772,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck_derive"
|
name = "bytemuck_derive"
|
||||||
version = "1.9.3"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
|
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -856,9 +854,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.29"
|
version = "1.2.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
|
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1075,7 +1073,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collections"
|
name = "collections"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
@@ -1476,7 +1474,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_refineable"
|
name = "derive_refineable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2328,7 +2326,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui"
|
name = "gpui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"as-raw-xcb-connection",
|
"as-raw-xcb-connection",
|
||||||
@@ -2394,7 +2392,7 @@ dependencies = [
|
|||||||
"slotmap",
|
"slotmap",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
"strum 0.27.1",
|
"strum 0.27.2",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"taffy",
|
"taffy",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
@@ -2421,7 +2419,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui_macros"
|
name = "gpui_macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -2433,7 +2431,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui_tokio"
|
name = "gpui_tokio"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2443,9 +2441,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grid"
|
name = "grid"
|
||||||
version = "0.13.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c"
|
checksum = "71b01d27060ad58be4663b9e4ac9e2d4806918e8876af8912afbddd1a91d5eaa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
@@ -2655,13 +2653,14 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "http_client"
|
name = "http_client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures",
|
"futures",
|
||||||
"http",
|
"http",
|
||||||
|
"http-body",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2672,7 +2671,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "http_client_tls"
|
name = "http_client_tls"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-platform-verifier",
|
"rustls-platform-verifier",
|
||||||
@@ -2740,9 +2739,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.15"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
|
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -2756,7 +2755,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -3048,9 +3047,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.7.8"
|
version = "0.7.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
|
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -3194,11 +3193,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c"
|
checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
"euclid",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3272,9 +3272,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.4"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
|
checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -3449,7 +3449,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "media"
|
name = "media"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bindgen 0.71.1",
|
"bindgen 0.71.1",
|
||||||
@@ -3672,7 +3672,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostr"
|
name = "nostr"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b"
|
source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -3695,7 +3695,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostr-connect"
|
name = "nostr-connect"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b"
|
source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-utility",
|
"async-utility",
|
||||||
"nostr",
|
"nostr",
|
||||||
@@ -3707,7 +3707,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostr-database"
|
name = "nostr-database"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b"
|
source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flatbuffers",
|
"flatbuffers",
|
||||||
"lru",
|
"lru",
|
||||||
@@ -3718,7 +3718,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostr-lmdb"
|
name = "nostr-lmdb"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b"
|
source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-utility",
|
"async-utility",
|
||||||
"heed",
|
"heed",
|
||||||
@@ -3731,7 +3731,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostr-relay-pool"
|
name = "nostr-relay-pool"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b"
|
source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-utility",
|
"async-utility",
|
||||||
"async-wsocket",
|
"async-wsocket",
|
||||||
@@ -3747,7 +3747,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostr-sdk"
|
name = "nostr-sdk"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b"
|
source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-utility",
|
"async-utility",
|
||||||
"nostr",
|
"nostr",
|
||||||
@@ -4062,7 +4062,7 @@ dependencies = [
|
|||||||
"num",
|
"num",
|
||||||
"num-bigint-dig",
|
"num-bigint-dig",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"subtle",
|
"subtle",
|
||||||
@@ -4364,17 +4364,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polling"
|
name = "polling"
|
||||||
version = "3.8.0"
|
version = "3.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50"
|
checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"tracing",
|
"windows-sys 0.60.2",
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4539,7 +4538,7 @@ dependencies = [
|
|||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -4555,7 +4554,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"lru-slab",
|
"lru-slab",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -4576,7 +4575,7 @@ dependencies = [
|
|||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@@ -4609,9 +4608,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.9.1"
|
version = "0.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"rand_core 0.9.3",
|
"rand_core 0.9.3",
|
||||||
@@ -4761,9 +4760,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.13"
|
version = "0.5.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
@@ -4802,7 +4801,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "refineable"
|
name = "refineable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_refineable",
|
"derive_refineable",
|
||||||
"workspace-hack",
|
"workspace-hack",
|
||||||
@@ -4953,7 +4952,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest_client"
|
name = "reqwest_client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -5308,7 +5307,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "scap"
|
name = "scap"
|
||||||
version = "0.0.8"
|
version = "0.0.8"
|
||||||
source = "git+https://github.com/zed-industries/scap?rev=270538dc780f5240723233ff901e1054641ed318#270538dc780f5240723233ff901e1054641ed318"
|
source = "git+https://github.com/zed-industries/scap?rev=808aa5c45b41e8f44729d02e38fd00a2fe2722e7#808aa5c45b41e8f44729d02e38fd00a2fe2722e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cocoa 0.25.0",
|
"cocoa 0.25.0",
|
||||||
@@ -5479,7 +5478,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "semantic_version"
|
name = "semantic_version"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5534,9 +5533,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.140"
|
version = "1.0.141"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
@@ -5772,6 +5771,16 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@@ -5822,11 +5831,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.27.1"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strum_macros 0.27.1",
|
"strum_macros 0.27.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5844,14 +5853,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum_macros"
|
name = "strum_macros"
|
||||||
version = "0.27.1"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
|
||||||
"syn 2.0.104",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5864,7 +5872,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sum_tree"
|
name = "sum_tree"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"log",
|
"log",
|
||||||
@@ -6087,13 +6095,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "taffy"
|
name = "taffy"
|
||||||
version = "0.4.4"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ec17858c2d465b2f734b798b920818a974faf0babb15d7fef81818a4b2d16f1"
|
checksum = "7aaef0ac998e6527d6d0d5582f7e43953bb17221ac75bb8eb2fcc2db3396db1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"grid",
|
"grid",
|
||||||
"num-traits",
|
|
||||||
"serde",
|
"serde",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
]
|
]
|
||||||
@@ -6297,7 +6304,7 @@ dependencies = [
|
|||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@@ -6605,7 +6612,7 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"sha1",
|
"sha1",
|
||||||
@@ -6838,7 +6845,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "util"
|
name = "util"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7"
|
source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-fs",
|
"async-fs",
|
||||||
@@ -7231,14 +7238,14 @@ version = "0.26.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e"
|
checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"webpki-root-certs 1.0.1",
|
"webpki-root-certs 1.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-root-certs"
|
name = "webpki-root-certs"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e"
|
checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
@@ -7249,14 +7256,14 @@ version = "0.26.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"webpki-roots 1.0.1",
|
"webpki-roots 1.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|||||||
3
assets/icons/open-url.svg
Normal file
3
assets/icons/open-url.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.25 14v3.05c0 1.12 0 1.68-.218 2.108a2 2 0 0 1-.874.874c-.428.218-.988.218-2.108.218h-8.1c-1.12 0-1.68 0-2.108-.218a2 2 0 0 1-.874-.874c-.218-.428-.218-.988-.218-2.108V8.875c0-1.05 0-1.574.192-1.98a2 2 0 0 1 .953-.953c.406-.192.93-.192 1.98-.192H9.25m4.5-2h6.5m0 0v6.5m0-6.5L11 13"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 489 B |
@@ -41,8 +41,17 @@ impl DisplayProfile for Profile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(pubkey) = self.public_key().to_bech32();
|
shorten_pubkey(self.public_key(), 4)
|
||||||
|
|
||||||
format!("{}:{}", &pubkey[0..4], &pubkey[pubkey.len() - 4..]).into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shorten_pubkey(public_key: PublicKey, len: usize) -> SharedString {
|
||||||
|
let Ok(pubkey) = public_key.to_bech32();
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}:{}",
|
||||||
|
&pubkey[0..(len + 1)],
|
||||||
|
&pubkey[pubkey.len() - len..]
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use ui::modal::ModalButtonProps;
|
|||||||
use ui::{ContextModal, IconName, Root, Sizable, StyledExt, TitleBar};
|
use ui::{ContextModal, IconName, Root, Sizable, StyledExt, TitleBar};
|
||||||
|
|
||||||
use crate::views::chat::{self, Chat};
|
use crate::views::chat::{self, Chat};
|
||||||
|
use crate::views::screening::Screening;
|
||||||
use crate::views::user_profile::UserProfile;
|
use crate::views::user_profile::UserProfile;
|
||||||
use crate::views::{
|
use crate::views::{
|
||||||
login, new_account, onboarding, preferences, sidebar, startup, user_profile, welcome,
|
login, new_account, onboarding, preferences, sidebar, startup, user_profile, welcome,
|
||||||
@@ -73,7 +74,7 @@ pub struct ChatSpace {
|
|||||||
dock: Entity<DockArea>,
|
dock: Entity<DockArea>,
|
||||||
toolbar: bool,
|
toolbar: bool,
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
subscriptions: SmallVec<[Subscription; 5]>,
|
subscriptions: SmallVec<[Subscription; 6]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatSpace {
|
impl ChatSpace {
|
||||||
@@ -172,13 +173,20 @@ impl ChatSpace {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Automatically run on_load function from UserProfile
|
// Automatically run on_load function when UserProfile is created
|
||||||
subscriptions.push(cx.observe_new::<UserProfile>(|this, window, cx| {
|
subscriptions.push(cx.observe_new::<UserProfile>(|this, window, cx| {
|
||||||
if let Some(window) = window {
|
if let Some(window) = window {
|
||||||
this.on_load(window, cx);
|
this.on_load(window, cx);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Automatically run on_load function when Screening is created
|
||||||
|
subscriptions.push(cx.observe_new::<Screening>(|this, window, cx| {
|
||||||
|
if let Some(window) = window {
|
||||||
|
this.on_load(window, cx);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
// Subscribe to open chat room requests
|
// Subscribe to open chat room requests
|
||||||
subscriptions.push(cx.subscribe_in(
|
subscriptions.push(cx.subscribe_in(
|
||||||
®istry,
|
®istry,
|
||||||
|
|||||||
@@ -405,10 +405,11 @@ async fn handle_nostr_notifications(
|
|||||||
.kind(Kind::InboxRelays)
|
.kind(Kind::InboxRelays)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if client.subscribe(filter, Some(opts)).await.is_ok() {
|
if let Ok(output) = client.subscribe(filter, Some(opts)).await {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Subscribed to get DM relays: {}",
|
"Subscribed to get DM relays: {} - Relays: {:?}",
|
||||||
event.pubkey.to_bech32().unwrap()
|
event.pubkey.to_bech32().unwrap(),
|
||||||
|
output.success
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub mod new_account;
|
|||||||
pub mod onboarding;
|
pub mod onboarding;
|
||||||
pub mod preferences;
|
pub mod preferences;
|
||||||
pub mod relays;
|
pub mod relays;
|
||||||
|
pub mod screening;
|
||||||
pub mod sidebar;
|
pub mod sidebar;
|
||||||
pub mod startup;
|
pub mod startup;
|
||||||
pub mod user_profile;
|
pub mod user_profile;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ impl Preferences {
|
|||||||
|
|
||||||
fn open_edit_profile(&self, window: &mut Window, cx: &mut Context<Self>) {
|
fn open_edit_profile(&self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let edit_profile = edit_profile::init(window, cx);
|
let edit_profile = edit_profile::init(window, cx);
|
||||||
let title = SharedString::new(t!("preferences.modal_profile_title"));
|
let title = SharedString::new(t!("profile.title"));
|
||||||
|
|
||||||
window.open_modal(cx, move |modal, _window, _cx| {
|
window.open_modal(cx, move |modal, _window, _cx| {
|
||||||
modal
|
modal
|
||||||
|
|||||||
288
crates/coop/src/views/screening.rs
Normal file
288
crates/coop/src/views/screening.rs
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
use common::display::{shorten_pubkey, DisplayProfile};
|
||||||
|
use common::nip05::nip05_verify;
|
||||||
|
use global::nostr_client;
|
||||||
|
use gpui::prelude::FluentBuilder;
|
||||||
|
use gpui::{
|
||||||
|
div, relative, rems, App, AppContext, Context, Entity, IntoElement, ParentElement, Render,
|
||||||
|
SharedString, Styled, Task, Window,
|
||||||
|
};
|
||||||
|
use gpui_tokio::Tokio;
|
||||||
|
use i18n::t;
|
||||||
|
use identity::Identity;
|
||||||
|
use nostr_sdk::prelude::*;
|
||||||
|
use registry::Registry;
|
||||||
|
use settings::AppSettings;
|
||||||
|
use theme::ActiveTheme;
|
||||||
|
use ui::avatar::Avatar;
|
||||||
|
use ui::button::{Button, ButtonRounded, ButtonVariants};
|
||||||
|
use ui::{h_flex, v_flex, ContextModal, Icon, IconName, Sizable, StyledExt};
|
||||||
|
|
||||||
|
pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<Screening> {
|
||||||
|
Screening::new(public_key, window, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Screening {
|
||||||
|
public_key: PublicKey,
|
||||||
|
followed: bool,
|
||||||
|
connections: usize,
|
||||||
|
verified: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Screening {
|
||||||
|
pub fn new(public_key: PublicKey, _window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||||
|
cx.new(|_| Self {
|
||||||
|
public_key,
|
||||||
|
followed: false,
|
||||||
|
connections: 0,
|
||||||
|
verified: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_load(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
// Skip if user isn't logged in
|
||||||
|
let Some(identity) = Identity::read_global(cx).public_key() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let public_key = self.public_key;
|
||||||
|
|
||||||
|
let check_trust_score: Task<(bool, usize)> = cx.background_spawn(async move {
|
||||||
|
let client = nostr_client();
|
||||||
|
|
||||||
|
let follow = Filter::new()
|
||||||
|
.kind(Kind::ContactList)
|
||||||
|
.author(identity)
|
||||||
|
.pubkey(public_key)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
let connection = Filter::new()
|
||||||
|
.kind(Kind::ContactList)
|
||||||
|
.pubkey(public_key)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
let is_follow = client.database().count(follow).await.unwrap_or(0) >= 1;
|
||||||
|
let connects = client.database().count(connection).await.unwrap_or(0);
|
||||||
|
|
||||||
|
(is_follow, connects)
|
||||||
|
});
|
||||||
|
|
||||||
|
let verify_nip05 = if let Some(address) = self.address(cx) {
|
||||||
|
Some(Tokio::spawn(cx, async move {
|
||||||
|
nip05_verify(public_key, &address).await.unwrap_or(false)
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
|
let (followed, connections) = check_trust_score.await;
|
||||||
|
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.followed = followed;
|
||||||
|
this.connections = connections;
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Update the NIP05 verification status if user has NIP05 address
|
||||||
|
if let Some(task) = verify_nip05 {
|
||||||
|
if let Ok(verified) = task.await {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.verified = verified;
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn profile(&self, cx: &Context<Self>) -> Profile {
|
||||||
|
let registry = Registry::read_global(cx);
|
||||||
|
registry.get_person(&self.public_key, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn address(&self, cx: &Context<Self>) -> Option<String> {
|
||||||
|
self.profile(cx).metadata().nip05
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_njump(&mut self, _window: &mut Window, cx: &mut App) {
|
||||||
|
let Ok(bech32) = self.public_key.to_bech32();
|
||||||
|
cx.open_url(&format!("https://njump.me/{bech32}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
let public_key = self.public_key;
|
||||||
|
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||||
|
let client = nostr_client();
|
||||||
|
let builder = EventBuilder::report(
|
||||||
|
vec![Tag::public_key_report(public_key, Report::Impersonation)],
|
||||||
|
"scam/impersonation",
|
||||||
|
);
|
||||||
|
let _ = client.send_event_builder(builder).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.spawn_in(window, async move |_, cx| {
|
||||||
|
if task.await.is_ok() {
|
||||||
|
cx.update(|window, cx| {
|
||||||
|
window.close_modal(cx);
|
||||||
|
window.push_notification(t!("screening.report_msg"), cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Screening {
|
||||||
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let proxy = AppSettings::get_global(cx).settings.proxy_user_avatars;
|
||||||
|
let profile = self.profile(cx);
|
||||||
|
let shorten_pubkey = shorten_pubkey(profile.public_key(), 8);
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.w_full()
|
||||||
|
.px_4()
|
||||||
|
.pt_8()
|
||||||
|
.pb_4()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_3()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.text_center()
|
||||||
|
.child(Avatar::new(profile.avatar_url(proxy)).size(rems(4.)))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.font_semibold()
|
||||||
|
.line_height(relative(1.25))
|
||||||
|
.child(profile.display_name()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.p_1()
|
||||||
|
.flex_1()
|
||||||
|
.h_7()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.rounded_full()
|
||||||
|
.bg(cx.theme().elevated_surface_background)
|
||||||
|
.text_sm()
|
||||||
|
.truncate()
|
||||||
|
.text_ellipsis()
|
||||||
|
.text_center()
|
||||||
|
.line_height(relative(1.))
|
||||||
|
.child(shorten_pubkey),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("njump")
|
||||||
|
.label(t!("profile.njump"))
|
||||||
|
.secondary()
|
||||||
|
.small()
|
||||||
|
.rounded(ButtonRounded::Full)
|
||||||
|
.on_click(cx.listener(move |this, _e, window, cx| {
|
||||||
|
this.open_njump(window, cx);
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("report")
|
||||||
|
.tooltip(t!("screening.report"))
|
||||||
|
.icon(IconName::Info)
|
||||||
|
.danger_alt()
|
||||||
|
.small()
|
||||||
|
.rounded(ButtonRounded::Full)
|
||||||
|
.on_click(cx.listener(move |this, _e, window, cx| {
|
||||||
|
this.report(window, cx);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.when_some(self.address(cx), |this, address| {
|
||||||
|
this.child(h_flex().gap_2().map(|this| {
|
||||||
|
if self.verified {
|
||||||
|
this.text_sm()
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::CheckCircleFill)
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.text_color(cx.theme().icon_accent),
|
||||||
|
)
|
||||||
|
.child(div().flex_1().child(SharedString::new(t!(
|
||||||
|
"screening.verified",
|
||||||
|
address = address
|
||||||
|
))))
|
||||||
|
} else {
|
||||||
|
this.text_sm()
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::CheckCircleFill)
|
||||||
|
.small()
|
||||||
|
.text_color(cx.theme().icon_muted),
|
||||||
|
)
|
||||||
|
.child(div().flex_1().child(SharedString::new(t!(
|
||||||
|
"screening.not_verified",
|
||||||
|
address = address
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
.child(h_flex().gap_2().map(|this| {
|
||||||
|
if !self.followed {
|
||||||
|
this.text_sm()
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::CheckCircleFill)
|
||||||
|
.small()
|
||||||
|
.text_color(cx.theme().icon_muted),
|
||||||
|
)
|
||||||
|
.child(SharedString::new(t!("screening.not_contact")))
|
||||||
|
} else {
|
||||||
|
this.text_sm()
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::CheckCircleFill)
|
||||||
|
.small()
|
||||||
|
.text_color(cx.theme().icon_accent),
|
||||||
|
)
|
||||||
|
.child(SharedString::new(t!("screening.contact")))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.text_sm()
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::CheckCircleFill)
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.text_color({
|
||||||
|
if self.connections > 0 {
|
||||||
|
cx.theme().icon_accent
|
||||||
|
} else {
|
||||||
|
cx.theme().icon_muted
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map(|this| {
|
||||||
|
if self.connections > 0 {
|
||||||
|
this.child(SharedString::new(t!(
|
||||||
|
"screening.total_connections",
|
||||||
|
u = self.connections
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
this.child(SharedString::new(t!("screening.no_connections")))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use gpui::prelude::FluentBuilder;
|
|
||||||
use gpui::{
|
|
||||||
div, img, rems, App, ClickEvent, Div, InteractiveElement, IntoElement, ParentElement as _,
|
|
||||||
RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window,
|
|
||||||
};
|
|
||||||
use settings::AppSettings;
|
|
||||||
use theme::ActiveTheme;
|
|
||||||
use ui::avatar::Avatar;
|
|
||||||
use ui::StyledExt;
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct DisplayRoom {
|
|
||||||
ix: usize,
|
|
||||||
base: Div,
|
|
||||||
img: Option<SharedString>,
|
|
||||||
label: Option<SharedString>,
|
|
||||||
description: Option<SharedString>,
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
handler: Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisplayRoom {
|
|
||||||
pub fn new(ix: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
ix,
|
|
||||||
base: div().h_9().w_full().px_1p5(),
|
|
||||||
img: None,
|
|
||||||
label: None,
|
|
||||||
description: None,
|
|
||||||
handler: Rc::new(|_, _, _| {}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
|
||||||
self.label = Some(label.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn description(mut self, description: impl Into<SharedString>) -> Self {
|
|
||||||
self.description = Some(description.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn img(mut self, img: impl Into<SharedString>) -> Self {
|
|
||||||
self.img = Some(img.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_click(
|
|
||||||
mut self,
|
|
||||||
handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
|
||||||
) -> Self {
|
|
||||||
self.handler = Rc::new(handler);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for DisplayRoom {
|
|
||||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
||||||
let handler = self.handler.clone();
|
|
||||||
let hide_avatar = AppSettings::get_global(cx).settings.hide_user_avatars;
|
|
||||||
|
|
||||||
self.base
|
|
||||||
.id(self.ix)
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.text_sm()
|
|
||||||
.rounded(cx.theme().radius)
|
|
||||||
.when(!hide_avatar, |this| {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.size_6()
|
|
||||||
.rounded_full()
|
|
||||||
.overflow_hidden()
|
|
||||||
.map(|this| {
|
|
||||||
if let Some(path) = self.img {
|
|
||||||
this.child(Avatar::new(path).size(rems(1.5)))
|
|
||||||
} else {
|
|
||||||
this.child(
|
|
||||||
img("brand/avatar.png")
|
|
||||||
.rounded_full()
|
|
||||||
.size_6()
|
|
||||||
.into_any_element(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.when_some(self.label, |this, label| {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.line_clamp(1)
|
|
||||||
.text_ellipsis()
|
|
||||||
.font_medium()
|
|
||||||
.child(label),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.when_some(self.description, |this, description| {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_placeholder)
|
|
||||||
.child(description),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.hover(|this| this.bg(cx.theme().elevated_surface_background))
|
|
||||||
.on_click(move |ev, window, cx| handler(ev, window, cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
174
crates/coop/src/views/sidebar/list_item.rs
Normal file
174
crates/coop/src/views/sidebar/list_item.rs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use gpui::prelude::FluentBuilder;
|
||||||
|
use gpui::{
|
||||||
|
div, img, rems, App, ClickEvent, Div, InteractiveElement, IntoElement,
|
||||||
|
ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window,
|
||||||
|
};
|
||||||
|
use i18n::t;
|
||||||
|
use nostr_sdk::prelude::*;
|
||||||
|
use registry::room::RoomKind;
|
||||||
|
use settings::AppSettings;
|
||||||
|
use theme::ActiveTheme;
|
||||||
|
use ui::actions::OpenProfile;
|
||||||
|
use ui::avatar::Avatar;
|
||||||
|
use ui::context_menu::ContextMenuExt;
|
||||||
|
use ui::modal::ModalButtonProps;
|
||||||
|
use ui::{h_flex, ContextModal, StyledExt};
|
||||||
|
|
||||||
|
use crate::views::screening;
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct RoomListItem {
|
||||||
|
ix: usize,
|
||||||
|
base: Div,
|
||||||
|
public_key: PublicKey,
|
||||||
|
name: Option<SharedString>,
|
||||||
|
avatar: Option<SharedString>,
|
||||||
|
created_at: Option<SharedString>,
|
||||||
|
kind: Option<RoomKind>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
handler: Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoomListItem {
|
||||||
|
pub fn new(ix: usize, public_key: PublicKey) -> Self {
|
||||||
|
Self {
|
||||||
|
ix,
|
||||||
|
public_key,
|
||||||
|
base: h_flex().h_9().w_full().px_1p5(),
|
||||||
|
name: None,
|
||||||
|
avatar: None,
|
||||||
|
created_at: None,
|
||||||
|
kind: None,
|
||||||
|
handler: Rc::new(|_, _, _| {}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(mut self, name: impl Into<SharedString>) -> Self {
|
||||||
|
self.name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn created_at(mut self, created_at: impl Into<SharedString>) -> Self {
|
||||||
|
self.created_at = Some(created_at.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn avatar(mut self, avatar: impl Into<SharedString>) -> Self {
|
||||||
|
self.avatar = Some(avatar.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kind(mut self, kind: RoomKind) -> Self {
|
||||||
|
self.kind = Some(kind);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_click(
|
||||||
|
mut self,
|
||||||
|
handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.handler = Rc::new(handler);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for RoomListItem {
|
||||||
|
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
|
let public_key = self.public_key;
|
||||||
|
let kind = self.kind;
|
||||||
|
let handler = self.handler.clone();
|
||||||
|
let hide_avatar = AppSettings::get_global(cx).settings.hide_user_avatars;
|
||||||
|
let screening = AppSettings::get_global(cx).settings.screening;
|
||||||
|
|
||||||
|
self.base
|
||||||
|
.id(self.ix)
|
||||||
|
.gap_2()
|
||||||
|
.text_sm()
|
||||||
|
.rounded(cx.theme().radius)
|
||||||
|
.when(!hide_avatar, |this| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.size_6()
|
||||||
|
.rounded_full()
|
||||||
|
.overflow_hidden()
|
||||||
|
.map(|this| {
|
||||||
|
if let Some(img) = self.avatar {
|
||||||
|
this.child(Avatar::new(img).size(rems(1.5)))
|
||||||
|
} else {
|
||||||
|
this.child(
|
||||||
|
img("brand/avatar.png")
|
||||||
|
.rounded_full()
|
||||||
|
.size_6()
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_between()
|
||||||
|
.when_some(self.name, |this, name| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.line_clamp(1)
|
||||||
|
.text_ellipsis()
|
||||||
|
.truncate()
|
||||||
|
.font_medium()
|
||||||
|
.child(name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when_some(self.created_at, |this, ago| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().text_placeholder)
|
||||||
|
.child(ago),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.context_menu(move |this, _window, _cx| {
|
||||||
|
// TODO: add share chat room
|
||||||
|
this.menu(t!("profile.view"), Box::new(OpenProfile(public_key)))
|
||||||
|
})
|
||||||
|
.hover(|this| this.bg(cx.theme().elevated_surface_background))
|
||||||
|
.on_click(move |event, window, cx| {
|
||||||
|
let handler = handler.clone();
|
||||||
|
|
||||||
|
if let Some(kind) = kind {
|
||||||
|
if kind != RoomKind::Ongoing && screening {
|
||||||
|
let screening = screening::init(public_key, window, cx);
|
||||||
|
|
||||||
|
window.open_modal(cx, move |this, _window, _cx| {
|
||||||
|
let handler_clone = handler.clone();
|
||||||
|
|
||||||
|
this.confirm()
|
||||||
|
.child(screening.clone())
|
||||||
|
.button_props(
|
||||||
|
ModalButtonProps::default()
|
||||||
|
.cancel_text(t!("screening.ignore"))
|
||||||
|
.ok_text(t!("screening.response")),
|
||||||
|
)
|
||||||
|
.on_ok(move |event, window, cx| {
|
||||||
|
handler_clone(event, window, cx);
|
||||||
|
// true to close the modal
|
||||||
|
true
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handler(event, window, cx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handler(event, window, cx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ use std::time::Duration;
|
|||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use common::debounced_delay::DebouncedDelay;
|
use common::debounced_delay::DebouncedDelay;
|
||||||
use common::display::DisplayProfile;
|
use common::display::DisplayProfile;
|
||||||
use common::nip05::nip05_verify;
|
|
||||||
use element::DisplayRoom;
|
|
||||||
use global::constants::{BOOTSTRAP_RELAYS, DEFAULT_MODAL_WIDTH, SEARCH_RELAYS};
|
use global::constants::{BOOTSTRAP_RELAYS, DEFAULT_MODAL_WIDTH, SEARCH_RELAYS};
|
||||||
use global::nostr_client;
|
use global::nostr_client;
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
@@ -20,6 +18,7 @@ use gpui_tokio::Tokio;
|
|||||||
use i18n::t;
|
use i18n::t;
|
||||||
use identity::Identity;
|
use identity::Identity;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use list_item::RoomListItem;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use registry::room::{Room, RoomKind};
|
use registry::room::{Room, RoomKind};
|
||||||
use registry::{Registry, RoomEmitter};
|
use registry::{Registry, RoomEmitter};
|
||||||
@@ -37,7 +36,7 @@ use ui::{ContextModal, IconName, Selectable, Sizable, StyledExt};
|
|||||||
|
|
||||||
use crate::views::compose;
|
use crate::views::compose;
|
||||||
|
|
||||||
mod element;
|
mod list_item;
|
||||||
|
|
||||||
const FIND_DELAY: u64 = 600;
|
const FIND_DELAY: u64 = 600;
|
||||||
const FIND_LIMIT: usize = 10;
|
const FIND_LIMIT: usize = 10;
|
||||||
@@ -58,7 +57,6 @@ pub struct Sidebar {
|
|||||||
// Rooms
|
// Rooms
|
||||||
indicator: Entity<Option<RoomKind>>,
|
indicator: Entity<Option<RoomKind>>,
|
||||||
active_filter: Entity<RoomKind>,
|
active_filter: Entity<RoomKind>,
|
||||||
trusted_only: bool,
|
|
||||||
// GPUI
|
// GPUI
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
image_cache: Entity<RetainAllImageCache>,
|
image_cache: Entity<RetainAllImageCache>,
|
||||||
@@ -129,7 +127,6 @@ impl Sidebar {
|
|||||||
image_cache: RetainAllImageCache::new(cx),
|
image_cache: RetainAllImageCache::new(cx),
|
||||||
find_debouncer: DebouncedDelay::new(),
|
find_debouncer: DebouncedDelay::new(),
|
||||||
finding: false,
|
finding: false,
|
||||||
trusted_only: false,
|
|
||||||
cancel_handle,
|
cancel_handle,
|
||||||
indicator,
|
indicator,
|
||||||
active_filter,
|
active_filter,
|
||||||
@@ -153,10 +150,16 @@ impl Sidebar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn create_temp_room(identity: PublicKey, public_key: PublicKey) -> Result<Room, Error> {
|
async fn create_temp_room(identity: PublicKey, public_key: PublicKey) -> Result<Room, Error> {
|
||||||
|
let client = nostr_client();
|
||||||
let keys = Keys::generate();
|
let keys = Keys::generate();
|
||||||
let builder = EventBuilder::private_msg_rumor(public_key, "");
|
let builder = EventBuilder::private_msg_rumor(public_key, "");
|
||||||
let event = builder.build(identity).sign(&keys).await?;
|
let event = builder.build(identity).sign(&keys).await?;
|
||||||
let room = Room::new(&event).kind(RoomKind::Ongoing);
|
|
||||||
|
// Request to get user's metadata
|
||||||
|
Self::request_metadata(client, public_key).await?;
|
||||||
|
|
||||||
|
// Create a temporary room
|
||||||
|
let room = Room::new(&event).rearrange_by(identity);
|
||||||
|
|
||||||
Ok(room)
|
Ok(room)
|
||||||
}
|
}
|
||||||
@@ -165,7 +168,6 @@ impl Sidebar {
|
|||||||
let client = nostr_client();
|
let client = nostr_client();
|
||||||
let timeout = Duration::from_secs(2);
|
let timeout = Duration::from_secs(2);
|
||||||
let mut rooms: BTreeSet<Room> = BTreeSet::new();
|
let mut rooms: BTreeSet<Room> = BTreeSet::new();
|
||||||
let mut processed: BTreeSet<PublicKey> = BTreeSet::new();
|
|
||||||
|
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
.kind(Kind::Metadata)
|
.kind(Kind::Metadata)
|
||||||
@@ -177,24 +179,13 @@ impl Sidebar {
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
// Process to verify the search results
|
// Process to verify the search results
|
||||||
for event in events.into_iter() {
|
for event in events.into_iter().unique_by(|event| event.pubkey) {
|
||||||
if processed.contains(&event.pubkey) {
|
// Skip if author is match current user
|
||||||
|
if event.pubkey == identity {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processed.insert(event.pubkey);
|
|
||||||
|
|
||||||
let metadata = Metadata::from_json(event.content).unwrap_or_default();
|
|
||||||
|
|
||||||
// Skip if NIP-05 is not found
|
|
||||||
let Some(target) = metadata.nip05.as_ref() else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Skip if NIP-05 is not valid or failed to verify
|
|
||||||
if !nip05_verify(event.pubkey, target).await.unwrap_or(false) {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Return a temporary room
|
||||||
if let Ok(room) = Self::create_temp_room(identity, event.pubkey).await {
|
if let Ok(room) = Self::create_temp_room(identity, event.pubkey).await {
|
||||||
rooms.insert(room);
|
rooms.insert(room);
|
||||||
}
|
}
|
||||||
@@ -299,14 +290,8 @@ impl Sidebar {
|
|||||||
let address = query.to_owned();
|
let address = query.to_owned();
|
||||||
|
|
||||||
let task = Tokio::spawn(cx, async move {
|
let task = Tokio::spawn(cx, async move {
|
||||||
let client = nostr_client();
|
|
||||||
|
|
||||||
if let Ok(profile) = common::nip05::nip05_profile(&address).await {
|
if let Ok(profile) = common::nip05::nip05_profile(&address).await {
|
||||||
let public_key = profile.public_key;
|
Self::create_temp_room(identity, profile.public_key).await
|
||||||
// Request for user metadata
|
|
||||||
Self::request_metadata(client, public_key).await.ok();
|
|
||||||
// Return a temporary room
|
|
||||||
Self::create_temp_room(identity, public_key).await
|
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!(t!("sidebar.addr_error")))
|
Err(anyhow!(t!("sidebar.addr_error")))
|
||||||
}
|
}
|
||||||
@@ -362,11 +347,6 @@ impl Sidebar {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let task: Task<Result<Room, Error>> = cx.background_spawn(async move {
|
let task: Task<Result<Room, Error>> = cx.background_spawn(async move {
|
||||||
let client = nostr_client();
|
|
||||||
|
|
||||||
// Request metadata for this user
|
|
||||||
Self::request_metadata(client, public_key).await?;
|
|
||||||
|
|
||||||
// Create a gift wrap event to represent as room
|
// Create a gift wrap event to represent as room
|
||||||
Self::create_temp_room(identity, public_key).await
|
Self::create_temp_room(identity, public_key).await
|
||||||
});
|
});
|
||||||
@@ -544,11 +524,6 @@ impl Sidebar {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_trusted_only(&mut self, cx: &mut Context<Self>) {
|
|
||||||
self.trusted_only = !self.trusted_only;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_room(&mut self, id: u64, window: &mut Window, cx: &mut Context<Self>) {
|
fn open_room(&mut self, id: u64, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let room = if let Some(room) = Registry::read_global(cx).room(&id, cx) {
|
let room = if let Some(room) = Registry::read_global(cx).room(&id, cx) {
|
||||||
room
|
room
|
||||||
@@ -695,20 +670,19 @@ impl Sidebar {
|
|||||||
for ix in range {
|
for ix in range {
|
||||||
if let Some(room) = rooms.get(ix) {
|
if let Some(room) = rooms.get(ix) {
|
||||||
let this = room.read(cx);
|
let this = room.read(cx);
|
||||||
let id = this.id;
|
let handler = cx.listener({
|
||||||
let ago = this.ago();
|
let id = this.id;
|
||||||
let label = this.display_name(cx);
|
move |this, _, window, cx| {
|
||||||
let img = this.display_image(proxy, cx);
|
this.open_room(id, window, cx);
|
||||||
|
}
|
||||||
let handler = cx.listener(move |this, _, window, cx| {
|
|
||||||
this.open_room(id, window, cx);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
DisplayRoom::new(ix)
|
RoomListItem::new(ix, this.members[0])
|
||||||
.img(img)
|
.avatar(this.display_image(proxy, cx))
|
||||||
.label(label)
|
.name(this.display_name(cx))
|
||||||
.description(ago)
|
.created_at(this.ago())
|
||||||
|
.kind(this.kind)
|
||||||
.on_click(handler),
|
.on_click(handler),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -761,7 +735,7 @@ impl Render for Sidebar {
|
|||||||
if self.active_filter.read(cx) == &RoomKind::Ongoing {
|
if self.active_filter.read(cx) == &RoomKind::Ongoing {
|
||||||
registry.ongoing_rooms(cx)
|
registry.ongoing_rooms(cx)
|
||||||
} else {
|
} else {
|
||||||
registry.request_rooms(self.trusted_only, cx)
|
registry.request_rooms(cx)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -872,28 +846,10 @@ impl Render for Sidebar {
|
|||||||
.rounded(ButtonRounded::Full)
|
.rounded(ButtonRounded::Full)
|
||||||
.selected(!self.filter(&RoomKind::Ongoing, cx))
|
.selected(!self.filter(&RoomKind::Ongoing, cx))
|
||||||
.on_click(cx.listener(|this, _, _, cx| {
|
.on_click(cx.listener(|this, _, _, cx| {
|
||||||
this.set_filter(RoomKind::Unknown, cx);
|
this.set_filter(RoomKind::default(), cx);
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
.when(!self.filter(&RoomKind::Ongoing, cx), |this| {
|
|
||||||
this.child(
|
|
||||||
Button::new("trusted")
|
|
||||||
.tooltip(t!("sidebar.trusted_contacts_tooltip"))
|
|
||||||
.map(|this| {
|
|
||||||
if self.trusted_only {
|
|
||||||
this.icon(IconName::FilterFill)
|
|
||||||
} else {
|
|
||||||
this.icon(IconName::Filter)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.small()
|
|
||||||
.transparent()
|
|
||||||
.on_click(cx.listener(|this, _, _, cx| {
|
|
||||||
this.set_trusted_only(cx);
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.when(registry.loading, |this| {
|
.when(registry.loading, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
@@ -918,7 +874,7 @@ impl Render for Sidebar {
|
|||||||
)
|
)
|
||||||
.when(registry.loading, |this| {
|
.when(registry.loading, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
div().absolute().bottom_4().px_4().child(
|
div().absolute().bottom_4().px_4().w_full().child(
|
||||||
div()
|
div()
|
||||||
.p_1()
|
.p_1()
|
||||||
.w_full()
|
.w_full()
|
||||||
|
|||||||
@@ -188,10 +188,13 @@ impl Render for UserProfile {
|
|||||||
.when(!self.followed, |this| {
|
.when(!self.followed, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
div()
|
div()
|
||||||
|
.flex_none()
|
||||||
|
.w_32()
|
||||||
.p_1()
|
.p_1()
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.bg(cx.theme().surface_background)
|
.bg(cx.theme().elevated_surface_background)
|
||||||
.text_xs()
|
.text_xs()
|
||||||
|
.font_semibold()
|
||||||
.child(SharedString::new(t!("profile.unknown"))),
|
.child(SharedString::new(t!("profile.unknown"))),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
@@ -211,7 +214,7 @@ impl Render for UserProfile {
|
|||||||
.gap_1()
|
.gap_1()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.p_1p5()
|
.p_2()
|
||||||
.h_9()
|
.h_9()
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
.bg(cx.theme().elevated_surface_background)
|
.bg(cx.theme().elevated_surface_background)
|
||||||
@@ -246,15 +249,18 @@ impl Render for UserProfile {
|
|||||||
.text_color(cx.theme().text_muted)
|
.text_color(cx.theme().text_muted)
|
||||||
.child(SharedString::new(t!("profile.label_bio"))),
|
.child(SharedString::new(t!("profile.label_bio"))),
|
||||||
)
|
)
|
||||||
.when_some(profile.metadata().about, |this, bio| {
|
.child(
|
||||||
this.child(
|
div()
|
||||||
div()
|
.p_2()
|
||||||
.p_1p5()
|
.rounded_md()
|
||||||
.rounded_md()
|
.bg(cx.theme().elevated_surface_background)
|
||||||
.bg(cx.theme().elevated_surface_background)
|
.child(
|
||||||
.child(bio),
|
profile
|
||||||
)
|
.metadata()
|
||||||
}),
|
.about
|
||||||
|
.unwrap_or(t!("profile.no_bio").to_string()),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("open-njump")
|
Button::new("open-njump")
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub fn nostr_client() -> &'static Client {
|
|||||||
.automatic_authentication(true)
|
.automatic_authentication(true)
|
||||||
// Sleep after idle for 5 seconds
|
// Sleep after idle for 5 seconds
|
||||||
.sleep_when_idle(SleepWhenIdle::Enabled {
|
.sleep_when_idle(SleepWhenIdle::Enabled {
|
||||||
timeout: Duration::from_secs(5),
|
timeout: Duration::from_secs(10),
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientBuilder::default().database(lmdb).opts(opts).build()
|
ClientBuilder::default().database(lmdb).opts(opts).build()
|
||||||
|
|||||||
@@ -188,16 +188,10 @@ impl Registry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get all request rooms.
|
/// Get all request rooms.
|
||||||
pub fn request_rooms(&self, trusted_only: bool, cx: &App) -> Vec<Entity<Room>> {
|
pub fn request_rooms(&self, cx: &App) -> Vec<Entity<Room>> {
|
||||||
self.rooms
|
self.rooms
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|room| {
|
.filter(|room| room.read(cx).kind != RoomKind::Ongoing)
|
||||||
if trusted_only {
|
|
||||||
room.read(cx).kind == RoomKind::Trusted
|
|
||||||
} else {
|
|
||||||
room.read(cx).kind != RoomKind::Ongoing
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -274,7 +268,6 @@ impl Registry {
|
|||||||
let events = send_events.merge(recv_events);
|
let events = send_events.merge(recv_events);
|
||||||
|
|
||||||
let mut rooms: BTreeSet<Room> = BTreeSet::new();
|
let mut rooms: BTreeSet<Room> = BTreeSet::new();
|
||||||
let mut trusted_keys: BTreeSet<PublicKey> = BTreeSet::new();
|
|
||||||
|
|
||||||
// Process each event and group by room hash
|
// Process each event and group by room hash
|
||||||
for event in events
|
for event in events
|
||||||
@@ -291,20 +284,7 @@ impl Registry {
|
|||||||
let mut public_keys = event.tags.public_keys().copied().collect_vec();
|
let mut public_keys = event.tags.public_keys().copied().collect_vec();
|
||||||
public_keys.push(event.pubkey);
|
public_keys.push(event.pubkey);
|
||||||
|
|
||||||
let mut is_trust = trusted_keys.contains(&event.pubkey);
|
// Check if the current user has sent at least one message to this room
|
||||||
|
|
||||||
if !is_trust {
|
|
||||||
// Check if room's author is seen in any contact list
|
|
||||||
let filter = Filter::new().kind(Kind::ContactList).pubkey(event.pubkey);
|
|
||||||
// If room's author is seen at least once, mark as trusted
|
|
||||||
is_trust = client.database().count(filter).await.unwrap_or(0) >= 1;
|
|
||||||
|
|
||||||
if is_trust {
|
|
||||||
trusted_keys.insert(event.pubkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if current_user has sent a message to this room at least once
|
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
.kind(Kind::PrivateDirectMessage)
|
.kind(Kind::PrivateDirectMessage)
|
||||||
.author(public_key)
|
.author(public_key)
|
||||||
@@ -313,20 +293,13 @@ impl Registry {
|
|||||||
// If current user has sent a message at least once, mark as ongoing
|
// If current user has sent a message at least once, mark as ongoing
|
||||||
let is_ongoing = client.database().count(filter).await.unwrap_or(1) >= 1;
|
let is_ongoing = client.database().count(filter).await.unwrap_or(1) >= 1;
|
||||||
|
|
||||||
|
// Create a new room
|
||||||
|
let room = Room::new(&event).rearrange_by(public_key);
|
||||||
|
|
||||||
if is_ongoing {
|
if is_ongoing {
|
||||||
rooms.insert(
|
rooms.insert(room.kind(RoomKind::Ongoing));
|
||||||
Room::new(&event)
|
|
||||||
.kind(RoomKind::Ongoing)
|
|
||||||
.rearrange_by(public_key),
|
|
||||||
);
|
|
||||||
} else if is_trust {
|
|
||||||
rooms.insert(
|
|
||||||
Room::new(&event)
|
|
||||||
.kind(RoomKind::Trusted)
|
|
||||||
.rearrange_by(public_key),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
rooms.insert(Room::new(&event).rearrange_by(public_key));
|
rooms.insert(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +398,7 @@ impl Registry {
|
|||||||
self.sort(cx);
|
self.sort(cx);
|
||||||
} else {
|
} else {
|
||||||
let room = Room::new(&event)
|
let room = Room::new(&event)
|
||||||
.kind(RoomKind::Unknown)
|
.kind(RoomKind::default())
|
||||||
.rearrange_by(identity);
|
.rearrange_by(identity);
|
||||||
|
|
||||||
// Push the new room to the front of the list
|
// Push the new room to the front of the list
|
||||||
@@ -433,7 +406,7 @@ impl Registry {
|
|||||||
|
|
||||||
// Notify the UI about the new room
|
// Notify the UI about the new room
|
||||||
cx.defer_in(window, move |_this, _window, cx| {
|
cx.defer_in(window, move |_this, _window, cx| {
|
||||||
cx.emit(RoomEmitter::Request(RoomKind::Unknown));
|
cx.emit(RoomEmitter::Request(RoomKind::default()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ pub struct SendError {
|
|||||||
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
pub enum RoomKind {
|
pub enum RoomKind {
|
||||||
Ongoing,
|
Ongoing,
|
||||||
Trusted,
|
|
||||||
#[default]
|
#[default]
|
||||||
Unknown,
|
Request,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -104,7 +103,7 @@ impl Room {
|
|||||||
subject,
|
subject,
|
||||||
picture,
|
picture,
|
||||||
members,
|
members,
|
||||||
kind: RoomKind::Unknown,
|
kind: RoomKind::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub struct Settings {
|
|||||||
pub proxy_user_avatars: bool,
|
pub proxy_user_avatars: bool,
|
||||||
pub hide_user_avatars: bool,
|
pub hide_user_avatars: bool,
|
||||||
pub backup_messages: bool,
|
pub backup_messages: bool,
|
||||||
|
pub screening: bool,
|
||||||
pub auto_login: bool,
|
pub auto_login: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,10 +63,11 @@ impl AppSettings {
|
|||||||
|
|
||||||
fn new(cx: &mut Context<Self>) -> Self {
|
fn new(cx: &mut Context<Self>) -> Self {
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
media_server: Url::parse("https://nostrmedia.com").expect("it's fine"),
|
media_server: Url::parse("https://nostrmedia.com").unwrap(),
|
||||||
proxy_user_avatars: true,
|
proxy_user_avatars: true,
|
||||||
hide_user_avatars: false,
|
hide_user_avatars: false,
|
||||||
backup_messages: true,
|
backup_messages: true,
|
||||||
|
screening: true,
|
||||||
auto_login: false,
|
auto_login: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@ use std::ops::{Deref, DerefMut};
|
|||||||
use colors::{brand, hsl, neutral};
|
use colors::{brand, hsl, neutral};
|
||||||
use gpui::{px, App, Global, Hsla, Pixels, SharedString, Window, WindowAppearance};
|
use gpui::{px, App, Global, Hsla, Pixels, SharedString, Window, WindowAppearance};
|
||||||
|
|
||||||
|
use crate::colors::{danger, warning};
|
||||||
|
|
||||||
mod colors;
|
mod colors;
|
||||||
mod scale;
|
mod scale;
|
||||||
|
|
||||||
@@ -12,154 +14,91 @@ pub fn init(cx: &mut App) {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct ThemeColor {
|
pub struct ThemeColor {
|
||||||
/// Border color. Used for most borders, is usually a high contrast color.
|
// Surface colors
|
||||||
pub border: Hsla,
|
|
||||||
/// Border color. Used for deemphasized borders, like a visual divider between two sections
|
|
||||||
pub border_variant: Hsla,
|
|
||||||
/// Border color. Used for focused elements, like keyboard focused list item.
|
|
||||||
pub border_focused: Hsla,
|
|
||||||
/// Border color. Used for selected elements, like an active search filter or selected checkbox.
|
|
||||||
pub border_selected: Hsla,
|
|
||||||
/// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
|
|
||||||
pub border_transparent: Hsla,
|
|
||||||
/// Border color. Used for disabled elements, like a disabled input or button.
|
|
||||||
pub border_disabled: Hsla,
|
|
||||||
/// Background color. Used for elevated surfaces, like a context menu, popup, or dialog.
|
|
||||||
pub elevated_surface_background: Hsla,
|
|
||||||
/// Background color. Used for grounded surfaces like a panel or tab.
|
|
||||||
pub surface_background: Hsla,
|
|
||||||
/// Background color. Used for the app background and blank panels or windows.
|
|
||||||
pub background: Hsla,
|
pub background: Hsla,
|
||||||
/// Text color. Used for the foreground of an element.
|
pub surface_background: Hsla,
|
||||||
pub element_foreground: Hsla,
|
pub elevated_surface_background: Hsla,
|
||||||
/// Background color. Used for the background of an element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
|
|
||||||
///
|
|
||||||
/// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
|
|
||||||
pub element_background: Hsla,
|
|
||||||
/// Background color. Used for the hover state of an element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
|
|
||||||
pub element_hover: Hsla,
|
|
||||||
/// Background color. Used for the active state of an element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
|
|
||||||
pub element_active: Hsla,
|
|
||||||
/// Background color. Used for the selected state of an element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Selected states are triggered by the element being selected (or "activated") by the user.
|
|
||||||
///
|
|
||||||
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
|
|
||||||
pub element_selected: Hsla,
|
|
||||||
/// Background Color. Used for the disabled state of a element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
|
|
||||||
pub element_disabled: Hsla,
|
|
||||||
/// Text color. Used for the foreground of an secondary element.
|
|
||||||
pub secondary_foreground: Hsla,
|
|
||||||
/// Background color. Used for the background of an secondary element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Secondary elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
|
|
||||||
///
|
|
||||||
/// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
|
|
||||||
pub secondary_background: Hsla,
|
|
||||||
/// Background color. Used for the hover state of an secondary element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
|
|
||||||
pub secondary_hover: Hsla,
|
|
||||||
/// Background color. Used for the active state of an secondary element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
|
|
||||||
pub secondary_active: Hsla,
|
|
||||||
/// Background color. Used for the selected state of an secondary element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Selected states are triggered by the element being selected (or "activated") by the user.
|
|
||||||
///
|
|
||||||
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
|
|
||||||
pub secondary_selected: Hsla,
|
|
||||||
/// Background Color. Used for the disabled state of an secondary element that should have a different background than the surface it's on.
|
|
||||||
///
|
|
||||||
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
|
|
||||||
pub secondary_disabled: Hsla,
|
|
||||||
/// Background color. Used for the area that shows where a dragged element will be dropped.
|
|
||||||
pub drop_target_background: Hsla,
|
|
||||||
/// Used for the background of a ghost element that should have the same background as the surface it's on.
|
|
||||||
///
|
|
||||||
/// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
|
|
||||||
///
|
|
||||||
/// For an element that should have a different background than the surface it's on, use `element_background`.
|
|
||||||
pub ghost_element_background: Hsla,
|
|
||||||
/// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
|
|
||||||
///
|
|
||||||
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
|
|
||||||
pub ghost_element_hover: Hsla,
|
|
||||||
/// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
|
|
||||||
///
|
|
||||||
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
|
|
||||||
pub ghost_element_active: Hsla,
|
|
||||||
/// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
|
|
||||||
///
|
|
||||||
/// Selected states are triggered by the element being selected (or "activated") by the user.
|
|
||||||
///
|
|
||||||
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
|
|
||||||
pub ghost_element_selected: Hsla,
|
|
||||||
/// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
|
|
||||||
///
|
|
||||||
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
|
|
||||||
pub ghost_element_disabled: Hsla,
|
|
||||||
/// Text color. Default text color used for most text.
|
|
||||||
pub text: Hsla,
|
|
||||||
/// Text color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
|
|
||||||
pub text_muted: Hsla,
|
|
||||||
/// Text color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
|
|
||||||
pub text_placeholder: Hsla,
|
|
||||||
/// Text color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
|
|
||||||
pub text_accent: Hsla,
|
|
||||||
/// Fill color. Used for the default fill color of an icon.
|
|
||||||
pub icon: Hsla,
|
|
||||||
/// Fill color. Used for the muted or deemphasized fill color of an icon.
|
|
||||||
///
|
|
||||||
/// This might be used to show an icon in an inactive pane, or to deemphasize a series of icons to give them less visual weight.
|
|
||||||
pub icon_muted: Hsla,
|
|
||||||
/// Fill color. Used for the accent fill color of an icon.
|
|
||||||
///
|
|
||||||
/// This might be used to show when a toggleable icon button is selected.
|
|
||||||
pub icon_accent: Hsla,
|
|
||||||
/// The color of the scrollbar thumb.
|
|
||||||
pub scrollbar_thumb_background: Hsla,
|
|
||||||
/// The color of the scrollbar thumb when hovered over.
|
|
||||||
pub scrollbar_thumb_hover_background: Hsla,
|
|
||||||
/// The border color of the scrollbar thumb.
|
|
||||||
pub scrollbar_thumb_border: Hsla,
|
|
||||||
/// The background color of the scrollbar track.
|
|
||||||
pub scrollbar_track_background: Hsla,
|
|
||||||
/// The border color of the scrollbar track.
|
|
||||||
pub scrollbar_track_border: Hsla,
|
|
||||||
/// Background color. Used for the background of a panel
|
|
||||||
pub panel_background: Hsla,
|
pub panel_background: Hsla,
|
||||||
/// Border color. Used for outline border.
|
|
||||||
pub ring: Hsla,
|
|
||||||
/// Background color. Used for inactive tab.
|
|
||||||
pub tab_inactive_background: Hsla,
|
|
||||||
/// Background color. Used for hovered tab.
|
|
||||||
pub tab_hover_background: Hsla,
|
|
||||||
/// Background color. Used for active tab.
|
|
||||||
pub tab_active_background: Hsla,
|
|
||||||
/// Background color. Used for Title Bar.
|
|
||||||
pub title_bar: Hsla,
|
|
||||||
/// Border color. Used for Title Bar.
|
|
||||||
pub title_bar_border: Hsla,
|
|
||||||
/// Background color. Used for modal's overlay.
|
|
||||||
pub overlay: Hsla,
|
pub overlay: Hsla,
|
||||||
/// Background color. Used for cursor.
|
pub title_bar: Hsla,
|
||||||
pub cursor: Hsla,
|
pub title_bar_border: Hsla,
|
||||||
/// Window border color.
|
|
||||||
///
|
|
||||||
/// # Platform specific:
|
|
||||||
///
|
|
||||||
/// This is only works on Linux, other platforms we can't change the window border color.
|
|
||||||
pub window_border: Hsla,
|
pub window_border: Hsla,
|
||||||
|
|
||||||
|
// Border colors
|
||||||
|
pub border: Hsla,
|
||||||
|
pub border_variant: Hsla,
|
||||||
|
pub border_focused: Hsla,
|
||||||
|
pub border_selected: Hsla,
|
||||||
|
pub border_transparent: Hsla,
|
||||||
|
pub border_disabled: Hsla,
|
||||||
|
pub ring: Hsla,
|
||||||
|
|
||||||
|
// Text colors
|
||||||
|
pub text: Hsla,
|
||||||
|
pub text_muted: Hsla,
|
||||||
|
pub text_placeholder: Hsla,
|
||||||
|
pub text_accent: Hsla,
|
||||||
|
|
||||||
|
// Icon colors
|
||||||
|
pub icon: Hsla,
|
||||||
|
pub icon_muted: Hsla,
|
||||||
|
pub icon_accent: Hsla,
|
||||||
|
|
||||||
|
// Element colors
|
||||||
|
pub element_foreground: Hsla,
|
||||||
|
pub element_background: Hsla,
|
||||||
|
pub element_hover: Hsla,
|
||||||
|
pub element_active: Hsla,
|
||||||
|
pub element_selected: Hsla,
|
||||||
|
pub element_disabled: Hsla,
|
||||||
|
|
||||||
|
// Secondary element colors
|
||||||
|
pub secondary_foreground: Hsla,
|
||||||
|
pub secondary_background: Hsla,
|
||||||
|
pub secondary_hover: Hsla,
|
||||||
|
pub secondary_active: Hsla,
|
||||||
|
pub secondary_selected: Hsla,
|
||||||
|
pub secondary_disabled: Hsla,
|
||||||
|
|
||||||
|
// Danger element colors
|
||||||
|
pub danger_foreground: Hsla,
|
||||||
|
pub danger_background: Hsla,
|
||||||
|
pub danger_hover: Hsla,
|
||||||
|
pub danger_active: Hsla,
|
||||||
|
pub danger_selected: Hsla,
|
||||||
|
pub danger_disabled: Hsla,
|
||||||
|
|
||||||
|
// Warning element colors
|
||||||
|
pub warning_foreground: Hsla,
|
||||||
|
pub warning_background: Hsla,
|
||||||
|
pub warning_hover: Hsla,
|
||||||
|
pub warning_active: Hsla,
|
||||||
|
pub warning_selected: Hsla,
|
||||||
|
pub warning_disabled: Hsla,
|
||||||
|
|
||||||
|
// Ghost element colors
|
||||||
|
pub ghost_element_background: Hsla,
|
||||||
|
pub ghost_element_hover: Hsla,
|
||||||
|
pub ghost_element_active: Hsla,
|
||||||
|
pub ghost_element_selected: Hsla,
|
||||||
|
pub ghost_element_disabled: Hsla,
|
||||||
|
|
||||||
|
// Tab colors
|
||||||
|
pub tab_inactive_background: Hsla,
|
||||||
|
pub tab_hover_background: Hsla,
|
||||||
|
pub tab_active_background: Hsla,
|
||||||
|
|
||||||
|
// Scrollbar colors
|
||||||
|
pub scrollbar_thumb_background: Hsla,
|
||||||
|
pub scrollbar_thumb_hover_background: Hsla,
|
||||||
|
pub scrollbar_thumb_border: Hsla,
|
||||||
|
pub scrollbar_track_background: Hsla,
|
||||||
|
pub scrollbar_track_border: Hsla,
|
||||||
|
|
||||||
|
// Interactive colors
|
||||||
|
pub drop_target_background: Hsla,
|
||||||
|
pub cursor: Hsla,
|
||||||
|
pub selection: Hsla,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default colors for the theme.
|
/// The default colors for the theme.
|
||||||
@@ -171,55 +110,79 @@ impl ThemeColor {
|
|||||||
/// Themes that do not specify all colors are refined off of these defaults.
|
/// Themes that do not specify all colors are refined off of these defaults.
|
||||||
pub fn light() -> Self {
|
pub fn light() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
background: neutral().light().step_1(),
|
||||||
|
surface_background: neutral().light().step_2(),
|
||||||
|
elevated_surface_background: neutral().light().step_3(),
|
||||||
|
panel_background: gpui::white(),
|
||||||
|
overlay: neutral().light_alpha().step_3(),
|
||||||
|
title_bar: gpui::transparent_black(),
|
||||||
|
title_bar_border: gpui::transparent_black(),
|
||||||
|
window_border: hsl(240.0, 5.9, 78.0),
|
||||||
|
|
||||||
border: neutral().light().step_6(),
|
border: neutral().light().step_6(),
|
||||||
border_variant: neutral().light().step_5(),
|
border_variant: neutral().light().step_5(),
|
||||||
border_focused: brand().light().step_7(),
|
border_focused: brand().light().step_7(),
|
||||||
border_selected: brand().light().step_7(),
|
border_selected: brand().light().step_7(),
|
||||||
border_transparent: gpui::transparent_black(),
|
border_transparent: gpui::transparent_black(),
|
||||||
border_disabled: neutral().light().step_3(),
|
border_disabled: neutral().light().step_3(),
|
||||||
elevated_surface_background: neutral().light().step_3(),
|
ring: brand().light().step_8(),
|
||||||
surface_background: neutral().light().step_2(),
|
|
||||||
background: neutral().light().step_1(),
|
text: neutral().light().step_12(),
|
||||||
|
text_muted: neutral().light().step_11(),
|
||||||
|
text_placeholder: neutral().light().step_10(),
|
||||||
|
text_accent: brand().light().step_11(),
|
||||||
|
|
||||||
|
icon: neutral().light().step_11(),
|
||||||
|
icon_muted: neutral().light().step_10(),
|
||||||
|
icon_accent: brand().light().step_11(),
|
||||||
|
|
||||||
element_foreground: brand().light().step_12(),
|
element_foreground: brand().light().step_12(),
|
||||||
element_background: brand().light().step_9(),
|
element_background: brand().light().step_9(),
|
||||||
element_hover: brand().light_alpha().step_10(),
|
element_hover: brand().light_alpha().step_10(),
|
||||||
element_active: brand().light().step_10(),
|
element_active: brand().light().step_10(),
|
||||||
element_selected: brand().light().step_11(),
|
element_selected: brand().light().step_11(),
|
||||||
element_disabled: brand().light_alpha().step_3(),
|
element_disabled: brand().light_alpha().step_3(),
|
||||||
|
|
||||||
secondary_foreground: brand().light().step_12(),
|
secondary_foreground: brand().light().step_12(),
|
||||||
secondary_background: brand().light().step_3(),
|
secondary_background: brand().light().step_3(),
|
||||||
secondary_hover: brand().light_alpha().step_4(),
|
secondary_hover: brand().light_alpha().step_4(),
|
||||||
secondary_active: brand().light().step_5(),
|
secondary_active: brand().light().step_5(),
|
||||||
secondary_selected: brand().light().step_5(),
|
secondary_selected: brand().light().step_5(),
|
||||||
secondary_disabled: brand().light_alpha().step_3(),
|
secondary_disabled: brand().light_alpha().step_3(),
|
||||||
drop_target_background: brand().light_alpha().step_2(),
|
|
||||||
|
danger_foreground: danger().light().step_1(),
|
||||||
|
danger_background: danger().light().step_9(),
|
||||||
|
danger_hover: danger().light_alpha().step_10(),
|
||||||
|
danger_active: danger().light().step_10(),
|
||||||
|
danger_selected: danger().light().step_11(),
|
||||||
|
danger_disabled: danger().light_alpha().step_3(),
|
||||||
|
|
||||||
|
warning_foreground: warning().light().step_12(),
|
||||||
|
warning_background: warning().light().step_9(),
|
||||||
|
warning_hover: warning().light_alpha().step_10(),
|
||||||
|
warning_active: warning().light().step_10(),
|
||||||
|
warning_selected: warning().light().step_11(),
|
||||||
|
warning_disabled: warning().light_alpha().step_3(),
|
||||||
|
|
||||||
ghost_element_background: gpui::transparent_black(),
|
ghost_element_background: gpui::transparent_black(),
|
||||||
ghost_element_hover: neutral().light_alpha().step_3(),
|
ghost_element_hover: neutral().light_alpha().step_3(),
|
||||||
ghost_element_active: neutral().light_alpha().step_4(),
|
ghost_element_active: neutral().light_alpha().step_5(),
|
||||||
ghost_element_selected: neutral().light_alpha().step_5(),
|
ghost_element_selected: neutral().light_alpha().step_5(),
|
||||||
ghost_element_disabled: neutral().light_alpha().step_2(),
|
ghost_element_disabled: neutral().light_alpha().step_2(),
|
||||||
text: neutral().light().step_12(),
|
|
||||||
text_muted: neutral().light().step_11(),
|
tab_inactive_background: neutral().light().step_3(),
|
||||||
text_placeholder: neutral().light().step_10(),
|
tab_hover_background: neutral().light().step_4(),
|
||||||
text_accent: brand().light().step_11(),
|
tab_active_background: neutral().light().step_5(),
|
||||||
icon: neutral().light().step_11(),
|
|
||||||
icon_muted: neutral().light().step_10(),
|
|
||||||
icon_accent: brand().light().step_11(),
|
|
||||||
scrollbar_thumb_background: neutral().light_alpha().step_3(),
|
scrollbar_thumb_background: neutral().light_alpha().step_3(),
|
||||||
scrollbar_thumb_hover_background: neutral().light_alpha().step_4(),
|
scrollbar_thumb_hover_background: neutral().light_alpha().step_4(),
|
||||||
scrollbar_thumb_border: gpui::transparent_black(),
|
scrollbar_thumb_border: gpui::transparent_black(),
|
||||||
scrollbar_track_background: gpui::transparent_black(),
|
scrollbar_track_background: gpui::transparent_black(),
|
||||||
scrollbar_track_border: neutral().light().step_5(),
|
scrollbar_track_border: neutral().light().step_5(),
|
||||||
panel_background: gpui::white(),
|
|
||||||
ring: brand().light().step_8(),
|
drop_target_background: brand().light_alpha().step_2(),
|
||||||
tab_active_background: neutral().light().step_5(),
|
|
||||||
tab_hover_background: neutral().light().step_4(),
|
|
||||||
tab_inactive_background: neutral().light().step_3(),
|
|
||||||
title_bar: gpui::transparent_black(),
|
|
||||||
title_bar_border: gpui::transparent_black(),
|
|
||||||
overlay: neutral().light_alpha().step_3(),
|
|
||||||
cursor: hsl(200., 100., 50.),
|
cursor: hsl(200., 100., 50.),
|
||||||
window_border: hsl(240.0, 5.9, 78.0),
|
selection: hsl(200., 100., 50.).alpha(0.25),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,55 +191,79 @@ impl ThemeColor {
|
|||||||
/// Themes that do not specify all colors are refined off of these defaults.
|
/// Themes that do not specify all colors are refined off of these defaults.
|
||||||
pub fn dark() -> Self {
|
pub fn dark() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
background: neutral().dark().step_1(),
|
||||||
|
surface_background: neutral().dark().step_2(),
|
||||||
|
elevated_surface_background: neutral().dark().step_3(),
|
||||||
|
panel_background: gpui::black(),
|
||||||
|
overlay: neutral().dark_alpha().step_3(),
|
||||||
|
title_bar: gpui::transparent_black(),
|
||||||
|
title_bar_border: gpui::transparent_black(),
|
||||||
|
window_border: hsl(240.0, 3.7, 28.0),
|
||||||
|
|
||||||
border: neutral().dark().step_6(),
|
border: neutral().dark().step_6(),
|
||||||
border_variant: neutral().dark().step_5(),
|
border_variant: neutral().dark().step_5(),
|
||||||
border_focused: brand().dark().step_7(),
|
border_focused: brand().dark().step_7(),
|
||||||
border_selected: brand().dark().step_7(),
|
border_selected: brand().dark().step_7(),
|
||||||
border_transparent: gpui::transparent_black(),
|
border_transparent: gpui::transparent_black(),
|
||||||
border_disabled: neutral().dark().step_3(),
|
border_disabled: neutral().dark().step_3(),
|
||||||
elevated_surface_background: neutral().dark().step_3(),
|
ring: brand().dark().step_8(),
|
||||||
surface_background: neutral().dark().step_2(),
|
|
||||||
background: neutral().dark().step_1(),
|
text: neutral().dark().step_12(),
|
||||||
|
text_muted: neutral().dark().step_11(),
|
||||||
|
text_placeholder: neutral().dark().step_10(),
|
||||||
|
text_accent: brand().dark().step_11(),
|
||||||
|
|
||||||
|
icon: neutral().dark().step_11(),
|
||||||
|
icon_muted: neutral().dark().step_10(),
|
||||||
|
icon_accent: brand().dark().step_11(),
|
||||||
|
|
||||||
element_foreground: brand().dark().step_1(),
|
element_foreground: brand().dark().step_1(),
|
||||||
element_background: brand().dark().step_9(),
|
element_background: brand().dark().step_9(),
|
||||||
element_hover: brand().dark_alpha().step_10(),
|
element_hover: brand().dark_alpha().step_10(),
|
||||||
element_active: brand().dark().step_10(),
|
element_active: brand().dark().step_10(),
|
||||||
element_selected: brand().dark().step_11(),
|
element_selected: brand().dark().step_11(),
|
||||||
element_disabled: brand().dark_alpha().step_3(),
|
element_disabled: brand().dark_alpha().step_3(),
|
||||||
|
|
||||||
secondary_foreground: brand().dark().step_12(),
|
secondary_foreground: brand().dark().step_12(),
|
||||||
secondary_background: brand().dark().step_3(),
|
secondary_background: brand().dark().step_3(),
|
||||||
secondary_hover: brand().dark_alpha().step_4(),
|
secondary_hover: brand().dark_alpha().step_4(),
|
||||||
secondary_active: brand().dark().step_5(),
|
secondary_active: brand().dark().step_5(),
|
||||||
secondary_selected: brand().dark().step_5(),
|
secondary_selected: brand().dark().step_5(),
|
||||||
secondary_disabled: brand().dark_alpha().step_3(),
|
secondary_disabled: brand().dark_alpha().step_3(),
|
||||||
drop_target_background: brand().dark_alpha().step_2(),
|
|
||||||
|
danger_foreground: danger().dark().step_1(),
|
||||||
|
danger_background: danger().dark().step_9(),
|
||||||
|
danger_hover: danger().dark_alpha().step_10(),
|
||||||
|
danger_active: danger().dark().step_10(),
|
||||||
|
danger_selected: danger().dark().step_11(),
|
||||||
|
danger_disabled: danger().dark_alpha().step_3(),
|
||||||
|
|
||||||
|
warning_foreground: warning().dark().step_12(),
|
||||||
|
warning_background: warning().dark().step_9(),
|
||||||
|
warning_hover: warning().dark_alpha().step_10(),
|
||||||
|
warning_active: warning().dark().step_10(),
|
||||||
|
warning_selected: warning().dark().step_11(),
|
||||||
|
warning_disabled: warning().dark_alpha().step_3(),
|
||||||
|
|
||||||
ghost_element_background: gpui::transparent_black(),
|
ghost_element_background: gpui::transparent_black(),
|
||||||
ghost_element_hover: neutral().dark_alpha().step_3(),
|
ghost_element_hover: neutral().dark_alpha().step_3(),
|
||||||
ghost_element_active: neutral().dark_alpha().step_4(),
|
ghost_element_active: neutral().dark_alpha().step_4(),
|
||||||
ghost_element_selected: neutral().dark_alpha().step_5(),
|
ghost_element_selected: neutral().dark_alpha().step_5(),
|
||||||
ghost_element_disabled: neutral().dark_alpha().step_2(),
|
ghost_element_disabled: neutral().dark_alpha().step_2(),
|
||||||
text: neutral().dark().step_12(),
|
|
||||||
text_muted: neutral().dark().step_11(),
|
tab_inactive_background: neutral().dark().step_3(),
|
||||||
text_placeholder: neutral().dark().step_10(),
|
tab_hover_background: neutral().dark().step_4(),
|
||||||
text_accent: brand().dark().step_11(),
|
tab_active_background: neutral().dark().step_5(),
|
||||||
icon: neutral().dark().step_11(),
|
|
||||||
icon_muted: neutral().dark().step_10(),
|
|
||||||
icon_accent: brand().dark().step_11(),
|
|
||||||
scrollbar_thumb_background: neutral().dark_alpha().step_3(),
|
scrollbar_thumb_background: neutral().dark_alpha().step_3(),
|
||||||
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
|
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
|
||||||
scrollbar_thumb_border: gpui::transparent_black(),
|
scrollbar_thumb_border: gpui::transparent_black(),
|
||||||
scrollbar_track_background: gpui::transparent_black(),
|
scrollbar_track_background: gpui::transparent_black(),
|
||||||
scrollbar_track_border: neutral().dark().step_5(),
|
scrollbar_track_border: neutral().dark().step_5(),
|
||||||
panel_background: gpui::black(),
|
|
||||||
ring: brand().dark().step_8(),
|
drop_target_background: brand().dark_alpha().step_2(),
|
||||||
tab_active_background: neutral().dark().step_5(),
|
|
||||||
tab_hover_background: neutral().dark().step_4(),
|
|
||||||
tab_inactive_background: neutral().dark().step_3(),
|
|
||||||
title_bar: gpui::transparent_black(),
|
|
||||||
title_bar_border: gpui::transparent_black(),
|
|
||||||
overlay: neutral().dark_alpha().step_3(),
|
|
||||||
cursor: hsl(200., 100., 50.),
|
cursor: hsl(200., 100., 50.),
|
||||||
window_border: hsl(240.0, 3.7, 28.0),
|
selection: hsl(200., 100., 50.).alpha(0.25),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,21 @@ pub trait ButtonVariants: Sized {
|
|||||||
self.with_variant(ButtonVariant::Secondary)
|
self.with_variant(ButtonVariant::Secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// With the danger style for the Button.
|
||||||
|
fn danger(self) -> Self {
|
||||||
|
self.with_variant(ButtonVariant::Danger)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With the danger alternate style for the Button.
|
||||||
|
fn danger_alt(self) -> Self {
|
||||||
|
self.with_variant(ButtonVariant::DangerAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With the warning style for the Button.
|
||||||
|
fn warning(self) -> Self {
|
||||||
|
self.with_variant(ButtonVariant::Warning)
|
||||||
|
}
|
||||||
|
|
||||||
/// With the ghost style for the Button.
|
/// With the ghost style for the Button.
|
||||||
fn ghost(self) -> Self {
|
fn ghost(self) -> Self {
|
||||||
self.with_variant(ButtonVariant::Ghost)
|
self.with_variant(ButtonVariant::Ghost)
|
||||||
@@ -88,6 +103,9 @@ impl ButtonCustomVariant {
|
|||||||
pub enum ButtonVariant {
|
pub enum ButtonVariant {
|
||||||
Primary,
|
Primary,
|
||||||
Secondary,
|
Secondary,
|
||||||
|
Danger,
|
||||||
|
DangerAlt,
|
||||||
|
Warning,
|
||||||
Ghost,
|
Ghost,
|
||||||
Transparent,
|
Transparent,
|
||||||
Custom(ButtonCustomVariant),
|
Custom(ButtonCustomVariant),
|
||||||
@@ -394,11 +412,19 @@ struct ButtonVariantStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ButtonVariant {
|
impl ButtonVariant {
|
||||||
|
fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||||
|
let bg = self.bg_color(window, cx);
|
||||||
|
let fg = self.text_color(window, cx);
|
||||||
|
|
||||||
|
ButtonVariantStyle { bg, fg }
|
||||||
|
}
|
||||||
|
|
||||||
fn bg_color(&self, _window: &Window, cx: &App) -> Hsla {
|
fn bg_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||||
match self {
|
match self {
|
||||||
ButtonVariant::Primary => cx.theme().element_background,
|
ButtonVariant::Primary => cx.theme().element_background,
|
||||||
ButtonVariant::Secondary => cx.theme().elevated_surface_background,
|
ButtonVariant::Secondary => cx.theme().elevated_surface_background,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Danger => cx.theme().danger_background,
|
||||||
|
ButtonVariant::Warning => cx.theme().warning_background,
|
||||||
ButtonVariant::Custom(colors) => colors.color,
|
ButtonVariant::Custom(colors) => colors.color,
|
||||||
_ => cx.theme().ghost_element_background,
|
_ => cx.theme().ghost_element_background,
|
||||||
}
|
}
|
||||||
@@ -408,23 +434,22 @@ impl ButtonVariant {
|
|||||||
match self {
|
match self {
|
||||||
ButtonVariant::Primary => cx.theme().element_foreground,
|
ButtonVariant::Primary => cx.theme().element_foreground,
|
||||||
ButtonVariant::Secondary => cx.theme().text_muted,
|
ButtonVariant::Secondary => cx.theme().text_muted,
|
||||||
|
ButtonVariant::Danger => cx.theme().danger_foreground,
|
||||||
|
ButtonVariant::DangerAlt => cx.theme().danger_background,
|
||||||
|
ButtonVariant::Warning => cx.theme().warning_foreground,
|
||||||
ButtonVariant::Transparent => cx.theme().text_placeholder,
|
ButtonVariant::Transparent => cx.theme().text_placeholder,
|
||||||
ButtonVariant::Ghost => cx.theme().text_muted,
|
ButtonVariant::Ghost => cx.theme().text_muted,
|
||||||
ButtonVariant::Custom(colors) => colors.foreground,
|
ButtonVariant::Custom(colors) => colors.foreground,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
|
||||||
let bg = self.bg_color(window, cx);
|
|
||||||
let fg = self.text_color(window, cx);
|
|
||||||
|
|
||||||
ButtonVariantStyle { bg, fg }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||||
let bg = match self {
|
let bg = match self {
|
||||||
ButtonVariant::Primary => cx.theme().element_hover,
|
ButtonVariant::Primary => cx.theme().element_hover,
|
||||||
ButtonVariant::Secondary => cx.theme().secondary_hover,
|
ButtonVariant::Secondary => cx.theme().secondary_hover,
|
||||||
|
ButtonVariant::Danger => cx.theme().danger_hover,
|
||||||
|
ButtonVariant::DangerAlt => gpui::transparent_black(),
|
||||||
|
ButtonVariant::Warning => cx.theme().warning_hover,
|
||||||
ButtonVariant::Ghost => cx.theme().ghost_element_hover,
|
ButtonVariant::Ghost => cx.theme().ghost_element_hover,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Transparent => gpui::transparent_black(),
|
||||||
ButtonVariant::Custom(colors) => colors.hover,
|
ButtonVariant::Custom(colors) => colors.hover,
|
||||||
@@ -444,6 +469,9 @@ impl ButtonVariant {
|
|||||||
let bg = match self {
|
let bg = match self {
|
||||||
ButtonVariant::Primary => cx.theme().element_active,
|
ButtonVariant::Primary => cx.theme().element_active,
|
||||||
ButtonVariant::Secondary => cx.theme().secondary_active,
|
ButtonVariant::Secondary => cx.theme().secondary_active,
|
||||||
|
ButtonVariant::Danger => cx.theme().danger_active,
|
||||||
|
ButtonVariant::DangerAlt => gpui::transparent_black(),
|
||||||
|
ButtonVariant::Warning => cx.theme().warning_active,
|
||||||
ButtonVariant::Ghost => cx.theme().ghost_element_active,
|
ButtonVariant::Ghost => cx.theme().ghost_element_active,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Transparent => gpui::transparent_black(),
|
||||||
ButtonVariant::Custom(colors) => colors.active,
|
ButtonVariant::Custom(colors) => colors.active,
|
||||||
@@ -462,6 +490,9 @@ impl ButtonVariant {
|
|||||||
let bg = match self {
|
let bg = match self {
|
||||||
ButtonVariant::Primary => cx.theme().element_selected,
|
ButtonVariant::Primary => cx.theme().element_selected,
|
||||||
ButtonVariant::Secondary => cx.theme().secondary_selected,
|
ButtonVariant::Secondary => cx.theme().secondary_selected,
|
||||||
|
ButtonVariant::Danger => cx.theme().danger_selected,
|
||||||
|
ButtonVariant::DangerAlt => gpui::transparent_black(),
|
||||||
|
ButtonVariant::Warning => cx.theme().warning_selected,
|
||||||
ButtonVariant::Ghost => cx.theme().ghost_element_selected,
|
ButtonVariant::Ghost => cx.theme().ghost_element_selected,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Transparent => gpui::transparent_black(),
|
||||||
ButtonVariant::Custom(colors) => colors.active,
|
ButtonVariant::Custom(colors) => colors.active,
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ pub enum IconName {
|
|||||||
ThumbsUp,
|
ThumbsUp,
|
||||||
Upload,
|
Upload,
|
||||||
UsersThreeFill,
|
UsersThreeFill,
|
||||||
|
OpenUrl,
|
||||||
WindowClose,
|
WindowClose,
|
||||||
WindowMaximize,
|
WindowMaximize,
|
||||||
WindowMinimize,
|
WindowMinimize,
|
||||||
@@ -140,6 +141,7 @@ impl IconName {
|
|||||||
Self::ThumbsUp => "icons/thumbs-up.svg",
|
Self::ThumbsUp => "icons/thumbs-up.svg",
|
||||||
Self::Upload => "icons/upload.svg",
|
Self::Upload => "icons/upload.svg",
|
||||||
Self::UsersThreeFill => "icons/users-three-fill.svg",
|
Self::UsersThreeFill => "icons/users-three-fill.svg",
|
||||||
|
Self::OpenUrl => "icons/open-url.svg",
|
||||||
Self::WindowClose => "icons/window-close.svg",
|
Self::WindowClose => "icons/window-close.svg",
|
||||||
Self::WindowMaximize => "icons/window-maximize.svg",
|
Self::WindowMaximize => "icons/window-maximize.svg",
|
||||||
Self::WindowMinimize => "icons/window-minimize.svg",
|
Self::WindowMinimize => "icons/window-minimize.svg",
|
||||||
|
|||||||
@@ -700,7 +700,7 @@ impl Element for TextElement {
|
|||||||
|
|
||||||
// Paint selections
|
// Paint selections
|
||||||
if let Some(path) = prepaint.selection_path.take() {
|
if let Some(path) = prepaint.selection_path.take() {
|
||||||
window.paint_path(path, cx.theme().element_disabled);
|
window.paint_path(path, cx.theme().selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paint text
|
// Paint text
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
141
locales/app.yml
141
locales/app.yml
@@ -548,7 +548,128 @@ subject:
|
|||||||
pt: "O assunto será atualizado quando você enviar uma mensagem."
|
pt: "O assunto será atualizado quando você enviar uma mensagem."
|
||||||
ko: "메시지를 보낼 때 제목이 업데이트됩니다."
|
ko: "메시지를 보낼 때 제목이 업데이트됩니다."
|
||||||
|
|
||||||
|
screening:
|
||||||
|
ignore:
|
||||||
|
en: "Ignore"
|
||||||
|
zh-CN: "忽略"
|
||||||
|
zh-TW: "忽略"
|
||||||
|
ru: "Игнорировать"
|
||||||
|
vi: "Bỏ qua"
|
||||||
|
ja: "無視"
|
||||||
|
es: "Ignorar"
|
||||||
|
pt: "Ignorar"
|
||||||
|
ko: "무시"
|
||||||
|
response:
|
||||||
|
en: "Response"
|
||||||
|
zh-CN: "回复"
|
||||||
|
zh-TW: "回覆"
|
||||||
|
ru: "Ответ"
|
||||||
|
vi: "Trả lời"
|
||||||
|
ja: "応答"
|
||||||
|
es: "Respuesta"
|
||||||
|
pt: "Resposta"
|
||||||
|
ko: "응답"
|
||||||
|
verified:
|
||||||
|
en: "%{address} matches the user's public key."
|
||||||
|
zh-CN: "%{address} 匹配用户的公钥。"
|
||||||
|
zh-TW: "%{address} 匹配用戶的公鑰。"
|
||||||
|
ru: "%{address} соответствует публичному ключу пользователя."
|
||||||
|
vi: "%{address} khớp với khóa công khai của người dùng."
|
||||||
|
ja: "%{address} はユーザーの公開鍵に一致します。"
|
||||||
|
es: "%{address} coincide con la clave pública del usuario."
|
||||||
|
pt: "%{address} corresponde à chave pública do usuário."
|
||||||
|
ko: "%{address}가 사용자의 공개 키와 일치합니다."
|
||||||
|
not_verified:
|
||||||
|
en: "%{address} does not match the user's public key."
|
||||||
|
zh-CN: "%{address} 不匹配用户的公钥。"
|
||||||
|
zh-TW: "%{address} 不匹配用戶的公鑰。"
|
||||||
|
ru: "%{address} не соответствует публичному ключу пользователя."
|
||||||
|
vi: "%{address} không khớp với khóa công khai của người dùng."
|
||||||
|
ja: "%{address} はユーザーの公開鍵に一致しません。"
|
||||||
|
es: "%{address} no coincide con la clave pública del usuario."
|
||||||
|
pt: "%{address} não corresponde à chave pública do usuário."
|
||||||
|
ko: "%{address}가 사용자의 공개 키와 일치하지 않습니다."
|
||||||
|
contact:
|
||||||
|
en: "This person is in your contact list."
|
||||||
|
zh-CN: "此人在您的联系人列表中。"
|
||||||
|
zh-TW: "此人位於您的聯絡人清單中。"
|
||||||
|
ru: "Этот человек находится в вашем списке контактов."
|
||||||
|
vi: "Người này có trong danh sách liên hệ của bạn."
|
||||||
|
ja: "この人はあなたの連絡先リストにあります。"
|
||||||
|
es: "Esta persona está en su lista de contactos."
|
||||||
|
pt: "Essa pessoa está na sua lista de contatos."
|
||||||
|
ko: "이 사람은 사용자의 연락처 목록에 있습니다."
|
||||||
|
not_contact:
|
||||||
|
en: "This person is not in your contact list."
|
||||||
|
zh-CN: "此人在您的联系人列表中。"
|
||||||
|
zh-TW: "此人位於您的聯絡人清單中。"
|
||||||
|
ru: "Этот человек находится в вашем списке контактов."
|
||||||
|
vi: "Người này có trong danh sách liên hệ của bạn."
|
||||||
|
ja: "この人はあなたの連絡先リストにあります。"
|
||||||
|
es: "Esta persona está en su lista de contactos."
|
||||||
|
pt: "Essa pessoa está na sua lista de contatos."
|
||||||
|
ko: "이 사람은 사용자의 연락처 목록에 있습니다."
|
||||||
|
total_connections:
|
||||||
|
en: "You have %{u} mutual contacts with this person."
|
||||||
|
zh-CN: "您与此人有 %{u} 个共同联系。"
|
||||||
|
zh-TW: "您與此人有 %{u} 個共同聯繫。"
|
||||||
|
ru: "Вы связаны с этим человеком %{u} раз."
|
||||||
|
vi: "Bạn có %{u} liên hệ chung với người này."
|
||||||
|
ja: "この人と共同の連絡先が %{u} 人あります。"
|
||||||
|
es: "Tienes %{u} contactos comunes con esta persona."
|
||||||
|
pt: "Você está vinculado a %{u} contatos com esta pessoa."
|
||||||
|
no_connections:
|
||||||
|
en: "You don’t have any mutual contacts with this person."
|
||||||
|
zh-CN: "您与此人没有任何共同联系。"
|
||||||
|
zh-TW: "您與此人沒有任何共同聯繫。"
|
||||||
|
ru: "Вы не связаны с этим человеком."
|
||||||
|
vi: "Bạn không có liên hệ chung với người này."
|
||||||
|
ja: "この人と共同の連絡先はありません。"
|
||||||
|
es: "No tienes contactos comunes con esta persona."
|
||||||
|
pt: "Você não está vinculado a nenhum contato com esta pessoa."
|
||||||
|
ko: "이 사람과 공통 연락처가 없습니다."
|
||||||
|
report:
|
||||||
|
en: "Report as a scam or impostor"
|
||||||
|
zh-CN: "举报为诈骗或冒充者"
|
||||||
|
zh-TW: "檢舉為詐騙或冒充者"
|
||||||
|
ru: "Сообщить о мошенничестве или подделке"
|
||||||
|
vi: "Báo cáo"
|
||||||
|
ja: "詐欺または冒充者として報告する"
|
||||||
|
es: "Informar como una estafa o impostor"
|
||||||
|
pt: "Relatar como uma estafa ou impostor"
|
||||||
|
ko: "신고를 해요"
|
||||||
|
report_msg:
|
||||||
|
en: "Report submitted successfully"
|
||||||
|
zh-CN: "举报成功"
|
||||||
|
zh-TW: "檢舉成功"
|
||||||
|
ru: "Жалоба отправлена успешно"
|
||||||
|
vi: "Báo cáo đã được gửi thành công"
|
||||||
|
ja: "報告が送信されました。"
|
||||||
|
es: "Informe enviado con éxito"
|
||||||
|
pt: "Relatório enviado com sucesso"
|
||||||
|
ko: "신고가 성공적으로 제출되었습니다."
|
||||||
|
|
||||||
profile:
|
profile:
|
||||||
|
title:
|
||||||
|
en: "Profile"
|
||||||
|
zh-CN: "个人资料"
|
||||||
|
zh-TW: "個人資料"
|
||||||
|
ru: "Профиль"
|
||||||
|
vi: "Hồ sơ"
|
||||||
|
ja: "プロフィール"
|
||||||
|
es: "Perfil"
|
||||||
|
pt: "Perfil"
|
||||||
|
ko: "프로필"
|
||||||
|
view:
|
||||||
|
en: "View Profile"
|
||||||
|
zh-CN: "查看个人资料"
|
||||||
|
zh-TW: "檢視個人資料"
|
||||||
|
ru: "Просмотреть профиль"
|
||||||
|
vi: "Xem hồ sơ"
|
||||||
|
ja: "プロフィールを表示する"
|
||||||
|
es: "Ver perfil"
|
||||||
|
pt: "Ver perfil"
|
||||||
|
ko: "프로필 보기"
|
||||||
set_profile_picture:
|
set_profile_picture:
|
||||||
en: "Set Profile Picture"
|
en: "Set Profile Picture"
|
||||||
zh-CN: "设置个人资料图片"
|
zh-CN: "设置个人资料图片"
|
||||||
@@ -639,18 +760,18 @@ profile:
|
|||||||
es: "Abrir en njump.me"
|
es: "Abrir en njump.me"
|
||||||
pt: "Abrir no njump.me"
|
pt: "Abrir no njump.me"
|
||||||
ko: "njump.me에서 열기"
|
ko: "njump.me에서 열기"
|
||||||
|
no_bio:
|
||||||
|
en: "No bio."
|
||||||
|
zh-CN: "无简介"
|
||||||
|
zh-TW: "無簡介"
|
||||||
|
ru: "Нет биографии"
|
||||||
|
vi: "Không có tiểu sử"
|
||||||
|
ja: "プロフィールなし"
|
||||||
|
es: "Sin biografía"
|
||||||
|
pt: "Sem biografia"
|
||||||
|
ko: "프로필 없음"
|
||||||
|
|
||||||
preferences:
|
preferences:
|
||||||
modal_profile_title:
|
|
||||||
en: "Profile"
|
|
||||||
zh-CN: "个人资料"
|
|
||||||
zh-TW: "個人資料"
|
|
||||||
ru: "Профиль"
|
|
||||||
vi: "Hồ sơ"
|
|
||||||
ja: "プロフィール"
|
|
||||||
es: "Perfil"
|
|
||||||
pt: "Perfil"
|
|
||||||
ko: "프로필"
|
|
||||||
modal_relays_title:
|
modal_relays_title:
|
||||||
en: "Edit your Messaging Relays"
|
en: "Edit your Messaging Relays"
|
||||||
zh-CN: "编辑您的Messaging Relays"
|
zh-CN: "编辑您的Messaging Relays"
|
||||||
|
|||||||
0
script/linux
Normal file → Executable file
0
script/linux
Normal file → Executable file
Reference in New Issue
Block a user